MDL-22077 mod_forum: Grammatical and formatting updates
[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
e2ede426
JL
183 /**
184 * Test get forum posts
185 */
186 public function test_mod_forum_get_forum_discussion_posts() {
d85bedf7 187 global $CFG, $PAGE;
e2ede426
JL
188
189 $this->resetAfterTest(true);
190
191 // Set the CFG variable to allow track forums.
192 $CFG->forum_trackreadposts = true;
193
194 // Create a user who can track forums.
195 $record = new stdClass();
196 $record->trackforums = true;
197 $user1 = self::getDataGenerator()->create_user($record);
198 // Create a bunch of other users to post.
199 $user2 = self::getDataGenerator()->create_user();
200 $user3 = self::getDataGenerator()->create_user();
201
202 // Set the first created user to the test user.
203 self::setUser($user1);
204
205 // Create course to add the module.
206 $course1 = self::getDataGenerator()->create_course();
207
208 // Forum with tracking off.
209 $record = new stdClass();
210 $record->course = $course1->id;
211 $record->trackingtype = FORUM_TRACKING_OFF;
212 $forum1 = self::getDataGenerator()->create_module('forum', $record);
213 $forum1context = context_module::instance($forum1->cmid);
214
db3c9ff8
PFO
215 // Forum with tracking enabled.
216 $record = new stdClass();
217 $record->course = $course1->id;
218 $forum2 = self::getDataGenerator()->create_module('forum', $record);
2256bb74 219 $forum2cm = get_coursemodule_from_id('forum', $forum2->cmid);
db3c9ff8
PFO
220 $forum2context = context_module::instance($forum2->cmid);
221
e2ede426
JL
222 // Add discussions to the forums.
223 $record = new stdClass();
224 $record->course = $course1->id;
225 $record->userid = $user1->id;
226 $record->forum = $forum1->id;
227 $discussion1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
228
229 $record = new stdClass();
230 $record->course = $course1->id;
231 $record->userid = $user2->id;
232 $record->forum = $forum1->id;
233 $discussion2 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
234
db3c9ff8
PFO
235 $record = new stdClass();
236 $record->course = $course1->id;
237 $record->userid = $user2->id;
238 $record->forum = $forum2->id;
239 $discussion3 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
240
e2ede426
JL
241 // Add 2 replies to the discussion 1 from different users.
242 $record = new stdClass();
243 $record->discussion = $discussion1->id;
244 $record->parent = $discussion1->firstpost;
245 $record->userid = $user2->id;
246 $discussion1reply1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record);
c8743f62
JL
247 $filename = 'shouldbeanimage.jpg';
248 // Add a fake inline image to the post.
249 $filerecordinline = array(
250 'contextid' => $forum1context->id,
251 'component' => 'mod_forum',
252 'filearea' => 'post',
253 'itemid' => $discussion1reply1->id,
254 'filepath' => '/',
255 'filename' => $filename,
256 );
257 $fs = get_file_storage();
258 $timepost = time();
259 $fs->create_file_from_string($filerecordinline, 'image contents (not really)');
e2ede426
JL
260
261 $record->parent = $discussion1reply1->id;
262 $record->userid = $user3->id;
263 $discussion1reply2 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record);
264
265 // Enrol the user in the course.
266 $enrol = enrol_get_plugin('manual');
267 // Following line enrol and assign default role id to the user.
268 // So the user automatically gets mod/forum:viewdiscussion on all forums of the course.
269 $this->getDataGenerator()->enrol_user($user1->id, $course1->id);
270 $this->getDataGenerator()->enrol_user($user2->id, $course1->id);
81f810dc
JL
271
272 // Delete one user, to test that we still receive posts by this user.
273 delete_user($user3);
e2ede426
JL
274
275 // Create what we expect to be returned when querying the discussion.
276 $expectedposts = array(
277 'posts' => array(),
b7ce46df
JL
278 'ratinginfo' => array(
279 'contextid' => $forum1context->id,
280 'component' => 'mod_forum',
281 'ratingarea' => 'post',
282 'canviewall' => null,
283 'canviewany' => null,
284 'scales' => array(),
285 'ratings' => array(),
286 ),
e2ede426
JL
287 'warnings' => array(),
288 );
694bf0c7 289
d85bedf7 290 // User pictures are initially empty, we should get the links once the external function is called.
e2ede426
JL
291 $expectedposts['posts'][] = array(
292 'id' => $discussion1reply2->id,
293 'discussion' => $discussion1reply2->discussion,
294 'parent' => $discussion1reply2->parent,
48fb0250 295 'userid' => (int) $discussion1reply2->userid,
e2ede426
JL
296 'created' => $discussion1reply2->created,
297 'modified' => $discussion1reply2->modified,
298 'mailed' => $discussion1reply2->mailed,
299 'subject' => $discussion1reply2->subject,
300 'message' => file_rewrite_pluginfile_urls($discussion1reply2->message, 'pluginfile.php',
301 $forum1context->id, 'mod_forum', 'post', $discussion1reply2->id),
48fb0250 302 'messageformat' => 1, // This value is usually changed by external_format_text() function.
e2ede426
JL
303 'messagetrust' => $discussion1reply2->messagetrust,
304 'attachment' => $discussion1reply2->attachment,
305 'totalscore' => $discussion1reply2->totalscore,
306 'mailnow' => $discussion1reply2->mailnow,
307 'children' => array(),
308 'canreply' => true,
309 'postread' => false,
694bf0c7 310 'userfullname' => fullname($user3),
3e95e09b
AN
311 'userpictureurl' => '',
312 'deleted' => false,
bc4c7337 313 'isprivatereply' => false,
e2ede426 314 );
694bf0c7 315
e2ede426
JL
316 $expectedposts['posts'][] = array(
317 'id' => $discussion1reply1->id,
318 'discussion' => $discussion1reply1->discussion,
319 'parent' => $discussion1reply1->parent,
48fb0250 320 'userid' => (int) $discussion1reply1->userid,
e2ede426
JL
321 'created' => $discussion1reply1->created,
322 'modified' => $discussion1reply1->modified,
323 'mailed' => $discussion1reply1->mailed,
324 'subject' => $discussion1reply1->subject,
325 'message' => file_rewrite_pluginfile_urls($discussion1reply1->message, 'pluginfile.php',
326 $forum1context->id, 'mod_forum', 'post', $discussion1reply1->id),
48fb0250 327 'messageformat' => 1, // This value is usually changed by external_format_text() function.
e2ede426
JL
328 'messagetrust' => $discussion1reply1->messagetrust,
329 'attachment' => $discussion1reply1->attachment,
c8743f62
JL
330 'messageinlinefiles' => array(
331 array(
332 'filename' => $filename,
333 'filepath' => '/',
334 'filesize' => '27',
335 'fileurl' => moodle_url::make_webservice_pluginfile_url($forum1context->id, 'mod_forum', 'post',
336 $discussion1reply1->id, '/', $filename),
337 'timemodified' => $timepost,
338 'mimetype' => 'image/jpeg',
1104a9fa 339 'isexternalfile' => false,
c8743f62
JL
340 )
341 ),
e2ede426
JL
342 'totalscore' => $discussion1reply1->totalscore,
343 'mailnow' => $discussion1reply1->mailnow,
d2c58b95 344 'children' => array($discussion1reply2->id),
e2ede426
JL
345 'canreply' => true,
346 'postread' => false,
694bf0c7 347 'userfullname' => fullname($user2),
3e95e09b
AN
348 'userpictureurl' => '',
349 'deleted' => false,
bc4c7337 350 'isprivatereply' => false,
e2ede426
JL
351 );
352
353 // Test a discussion with two additional posts (total 3 posts).
354 $posts = mod_forum_external::get_forum_discussion_posts($discussion1->id, 'modified', 'DESC');
355 $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts);
356 $this->assertEquals(3, count($posts['posts']));
357
d85bedf7
JL
358 // Generate here the pictures because we need to wait to the external function to init the theme.
359 $userpicture = new user_picture($user3);
360 $userpicture->size = 1; // Size f1.
361 $expectedposts['posts'][0]['userpictureurl'] = $userpicture->get_url($PAGE)->out(false);
362
363 $userpicture = new user_picture($user2);
364 $userpicture->size = 1; // Size f1.
365 $expectedposts['posts'][1]['userpictureurl'] = $userpicture->get_url($PAGE)->out(false);
366
e2ede426
JL
367 // Unset the initial discussion post.
368 array_pop($posts['posts']);
369 $this->assertEquals($expectedposts, $posts);
370
2256bb74
JL
371 // Check we receive the unread count correctly on tracked forum.
372 forum_tp_count_forum_unread_posts($forum2cm, $course1, true); // Reset static cache.
373 $result = mod_forum_external::get_forums_by_courses(array($course1->id));
374 $result = external_api::clean_returnvalue(mod_forum_external::get_forums_by_courses_returns(), $result);
375 foreach ($result as $f) {
376 if ($f['id'] == $forum2->id) {
377 $this->assertEquals(1, $f['unreadpostscount']);
378 }
379 }
380
e2ede426
JL
381 // Test discussion without additional posts. There should be only one post (the one created by the discussion).
382 $posts = mod_forum_external::get_forum_discussion_posts($discussion2->id, 'modified', 'DESC');
383 $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts);
384 $this->assertEquals(1, count($posts['posts']));
385
db3c9ff8
PFO
386 // Test discussion tracking on not tracked forum.
387 $result = mod_forum_external::view_forum_discussion($discussion1->id);
388 $result = external_api::clean_returnvalue(mod_forum_external::view_forum_discussion_returns(), $result);
389 $this->assertTrue($result['status']);
390 $this->assertEmpty($result['warnings']);
391
392 // Test posts have not been marked as read.
393 $posts = mod_forum_external::get_forum_discussion_posts($discussion1->id, 'modified', 'DESC');
394 $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts);
395 foreach ($posts['posts'] as $post) {
396 $this->assertFalse($post['postread']);
397 }
398
399 // Test discussion tracking on tracked forum.
400 $result = mod_forum_external::view_forum_discussion($discussion3->id);
401 $result = external_api::clean_returnvalue(mod_forum_external::view_forum_discussion_returns(), $result);
402 $this->assertTrue($result['status']);
403 $this->assertEmpty($result['warnings']);
404
405 // Test posts have been marked as read.
406 $posts = mod_forum_external::get_forum_discussion_posts($discussion3->id, 'modified', 'DESC');
407 $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts);
408 foreach ($posts['posts'] as $post) {
409 $this->assertTrue($post['postread']);
410 }
2256bb74
JL
411
412 // Check we receive 0 unread posts.
413 forum_tp_count_forum_unread_posts($forum2cm, $course1, true); // Reset static cache.
414 $result = mod_forum_external::get_forums_by_courses(array($course1->id));
415 $result = external_api::clean_returnvalue(mod_forum_external::get_forums_by_courses_returns(), $result);
416 foreach ($result as $f) {
417 if ($f['id'] == $forum2->id) {
418 $this->assertEquals(0, $f['unreadpostscount']);
419 }
420 }
e2ede426 421 }
c2586672 422
3e95e09b
AN
423 /**
424 * Test get forum posts
425 */
426 public function test_mod_forum_get_forum_discussion_posts_deleted() {
427 global $CFG, $PAGE;
428
429 $this->resetAfterTest(true);
430 $generator = self::getDataGenerator()->get_plugin_generator('mod_forum');
431
432 // Create a course and enrol some users in it.
433 $course1 = self::getDataGenerator()->create_course();
434
435 // Create users.
436 $user1 = self::getDataGenerator()->create_user();
437 $this->getDataGenerator()->enrol_user($user1->id, $course1->id);
438 $user2 = self::getDataGenerator()->create_user();
439 $this->getDataGenerator()->enrol_user($user2->id, $course1->id);
440
441 // Set the first created user to the test user.
442 self::setUser($user1);
443
444 // Create test data.
445 $forum1 = self::getDataGenerator()->create_module('forum', (object) [
446 'course' => $course1->id,
447 ]);
448 $forum1context = context_module::instance($forum1->cmid);
449
450 // Add discussions to the forum.
451 $discussion = $generator->create_discussion((object) [
452 'course' => $course1->id,
453 'userid' => $user1->id,
454 'forum' => $forum1->id,
455 ]);
456
457 $discussion2 = $generator->create_discussion((object) [
458 'course' => $course1->id,
459 'userid' => $user2->id,
460 'forum' => $forum1->id,
461 ]);
462
463 // Add replies to the discussion.
464 $discussionreply1 = $generator->create_post((object) [
465 'discussion' => $discussion->id,
466 'parent' => $discussion->firstpost,
467 'userid' => $user2->id,
468 ]);
469 $discussionreply2 = $generator->create_post((object) [
470 'discussion' => $discussion->id,
471 'parent' => $discussionreply1->id,
472 'userid' => $user2->id,
473 'subject' => '',
474 'message' => '',
475 'messageformat' => FORMAT_PLAIN,
476 'deleted' => 1,
477 ]);
478 $discussionreply3 = $generator->create_post((object) [
479 'discussion' => $discussion->id,
480 'parent' => $discussion->firstpost,
481 'userid' => $user2->id,
482 ]);
483
484 // Test where some posts have been marked as deleted.
485 $posts = mod_forum_external::get_forum_discussion_posts($discussion->id, 'modified', 'DESC');
486 $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts);
487 $deletedsubject = get_string('privacy:request:delete:post:subject', 'mod_forum');
488 $deletedmessage = get_string('privacy:request:delete:post:message', 'mod_forum');
489
490 foreach ($posts['posts'] as $post) {
491 if ($post['id'] == $discussionreply2->id) {
492 $this->assertTrue($post['deleted']);
493 $this->assertEquals($deletedsubject, $post['subject']);
494 $this->assertEquals($deletedmessage, $post['message']);
495 } else {
496 $this->assertFalse($post['deleted']);
497 $this->assertNotEquals($deletedsubject, $post['subject']);
498 $this->assertNotEquals($deletedmessage, $post['message']);
499 }
500 }
501 }
502
b1aa7dfa
JL
503 /**
504 * Test get forum posts (qanda forum)
505 */
506 public function test_mod_forum_get_forum_discussion_posts_qanda() {
507 global $CFG, $DB;
508
509 $this->resetAfterTest(true);
510
511 $record = new stdClass();
512 $user1 = self::getDataGenerator()->create_user($record);
513 $user2 = self::getDataGenerator()->create_user();
514
515 // Set the first created user to the test user.
516 self::setUser($user1);
517
518 // Create course to add the module.
519 $course1 = self::getDataGenerator()->create_course();
520 $this->getDataGenerator()->enrol_user($user1->id, $course1->id);
521 $this->getDataGenerator()->enrol_user($user2->id, $course1->id);
522
523 // Forum with tracking off.
524 $record = new stdClass();
525 $record->course = $course1->id;
526 $record->type = 'qanda';
527 $forum1 = self::getDataGenerator()->create_module('forum', $record);
528 $forum1context = context_module::instance($forum1->cmid);
529
530 // Add discussions to the forums.
531 $record = new stdClass();
532 $record->course = $course1->id;
533 $record->userid = $user2->id;
534 $record->forum = $forum1->id;
535 $discussion1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
536
537 // Add 1 reply (not the actual user).
538 $record = new stdClass();
539 $record->discussion = $discussion1->id;
540 $record->parent = $discussion1->firstpost;
541 $record->userid = $user2->id;
542 $discussion1reply1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record);
543
544 // We still see only the original post.
545 $posts = mod_forum_external::get_forum_discussion_posts($discussion1->id, 'modified', 'DESC');
546 $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts);
547 $this->assertEquals(1, count($posts['posts']));
548
549 // Add a new reply, the user is going to be able to see only the original post and their new post.
550 $record = new stdClass();
551 $record->discussion = $discussion1->id;
552 $record->parent = $discussion1->firstpost;
553 $record->userid = $user1->id;
554 $discussion1reply2 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record);
555
556 $posts = mod_forum_external::get_forum_discussion_posts($discussion1->id, 'modified', 'DESC');
557 $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts);
558 $this->assertEquals(2, count($posts['posts']));
559
560 // Now, we can fake the time of the user post, so he can se the rest of the discussion posts.
561 $discussion1reply2->created -= $CFG->maxeditingtime * 2;
562 $DB->update_record('forum_posts', $discussion1reply2);
563
564 $posts = mod_forum_external::get_forum_discussion_posts($discussion1->id, 'modified', 'DESC');
565 $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts);
566 $this->assertEquals(3, count($posts['posts']));
b1aa7dfa
JL
567 }
568
c2586672
JL
569 /**
570 * Test get forum discussions paginated
571 */
572 public function test_mod_forum_get_forum_discussions_paginated() {
d85bedf7 573 global $USER, $CFG, $DB, $PAGE;
c2586672
JL
574
575 $this->resetAfterTest(true);
576
577 // Set the CFG variable to allow track forums.
578 $CFG->forum_trackreadposts = true;
579
580 // Create a user who can track forums.
581 $record = new stdClass();
582 $record->trackforums = true;
583 $user1 = self::getDataGenerator()->create_user($record);
584 // Create a bunch of other users to post.
585 $user2 = self::getDataGenerator()->create_user();
586 $user3 = self::getDataGenerator()->create_user();
587 $user4 = self::getDataGenerator()->create_user();
588
589 // Set the first created user to the test user.
590 self::setUser($user1);
591
592 // Create courses to add the modules.
593 $course1 = self::getDataGenerator()->create_course();
594
595 // First forum with tracking off.
596 $record = new stdClass();
597 $record->course = $course1->id;
598 $record->trackingtype = FORUM_TRACKING_OFF;
599 $forum1 = self::getDataGenerator()->create_module('forum', $record);
600
601 // Add discussions to the forums.
602 $record = new stdClass();
603 $record->course = $course1->id;
604 $record->userid = $user1->id;
605 $record->forum = $forum1->id;
606 $discussion1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
607
608 // Add three replies to the discussion 1 from different users.
609 $record = new stdClass();
610 $record->discussion = $discussion1->id;
611 $record->parent = $discussion1->firstpost;
612 $record->userid = $user2->id;
613 $discussion1reply1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record);
614
615 $record->parent = $discussion1reply1->id;
616 $record->userid = $user3->id;
617 $discussion1reply2 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record);
618
619 $record->userid = $user4->id;
620 $discussion1reply3 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record);
621
622 // Enrol the user in the first course.
623 $enrol = enrol_get_plugin('manual');
624
625 // We don't use the dataGenerator as we need to get the $instance2 to unenrol later.
626 $enrolinstances = enrol_get_instances($course1->id, true);
627 foreach ($enrolinstances as $courseenrolinstance) {
628 if ($courseenrolinstance->enrol == "manual") {
629 $instance1 = $courseenrolinstance;
630 break;
631 }
632 }
633 $enrol->enrol_user($instance1, $user1->id);
634
81f810dc
JL
635 // Delete one user.
636 delete_user($user4);
637
c2586672
JL
638 // Assign capabilities to view discussions for forum 1.
639 $cm = get_coursemodule_from_id('forum', $forum1->cmid, 0, false, MUST_EXIST);
640 $context = context_module::instance($cm->id);
641 $newrole = create_role('Role 2', 'role2', 'Role 2 description');
642 $this->assignUserCapability('mod/forum:viewdiscussion', $context->id, $newrole);
643
644 // Create what we expect to be returned when querying the forums.
645
646 $post1 = $DB->get_record('forum_posts', array('id' => $discussion1->firstpost), '*', MUST_EXIST);
c2586672 647
d85bedf7 648 // User pictures are initially empty, we should get the links once the external function is called.
c2586672
JL
649 $expecteddiscussions = array(
650 'id' => $discussion1->firstpost,
651 'name' => $discussion1->name,
652 'groupid' => $discussion1->groupid,
653 'timemodified' => $discussion1reply3->created,
654 'usermodified' => $discussion1reply3->userid,
655 'timestart' => $discussion1->timestart,
656 'timeend' => $discussion1->timeend,
657 'discussion' => $discussion1->id,
658 'parent' => 0,
659 'userid' => $discussion1->userid,
660 'created' => $post1->created,
661 'modified' => $post1->modified,
662 'mailed' => $post1->mailed,
663 'subject' => $post1->subject,
664 'message' => $post1->message,
665 'messageformat' => $post1->messageformat,
666 'messagetrust' => $post1->messagetrust,
667 'attachment' => $post1->attachment,
668 'totalscore' => $post1->totalscore,
669 'mailnow' => $post1->mailnow,
670 'userfullname' => fullname($user1),
671 'usermodifiedfullname' => fullname($user4),
d85bedf7
JL
672 'userpictureurl' => '',
673 'usermodifiedpictureurl' => '',
c2586672 674 'numreplies' => 3,
5f219cf1 675 'numunread' => 0,
0f3bbfd4
AN
676 'pinned' => FORUM_DISCUSSION_UNPINNED,
677 'locked' => false,
678 'canreply' => false,
c2586672
JL
679 );
680
681 // Call the external function passing forum id.
682 $discussions = mod_forum_external::get_forum_discussions_paginated($forum1->id);
683 $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_paginated_returns(), $discussions);
684 $expectedreturn = array(
685 'discussions' => array($expecteddiscussions),
686 'warnings' => array()
687 );
d85bedf7
JL
688
689 // Wait the theme to be loaded (the external_api call does that) to generate the user profiles.
690 $userpicture = new user_picture($user1);
691 $userpicture->size = 1; // Size f1.
692 $expectedreturn['discussions'][0]['userpictureurl'] = $userpicture->get_url($PAGE)->out(false);
693
694 $userpicture = new user_picture($user4);
695 $userpicture->size = 1; // Size f1.
696 $expectedreturn['discussions'][0]['usermodifiedpictureurl'] = $userpicture->get_url($PAGE)->out(false);
697
c2586672
JL
698 $this->assertEquals($expectedreturn, $discussions);
699
700 // Call without required view discussion capability.
701 $this->unassignUserCapability('mod/forum:viewdiscussion', $context->id, $newrole);
702 try {
703 mod_forum_external::get_forum_discussions_paginated($forum1->id);
704 $this->fail('Exception expected due to missing capability.');
705 } catch (moodle_exception $e) {
706 $this->assertEquals('noviewdiscussionspermission', $e->errorcode);
707 }
708
709 // Unenrol user from second course.
710 $enrol->unenrol_user($instance1, $user1->id);
711
712 // Call for the second course we unenrolled the user from, make sure exception thrown.
713 try {
714 mod_forum_external::get_forum_discussions_paginated($forum1->id);
715 $this->fail('Exception expected due to being unenrolled from the course.');
716 } catch (moodle_exception $e) {
717 $this->assertEquals('requireloginerror', $e->errorcode);
718 }
719 }
039c81f0
JL
720
721 /**
722 * Test get forum discussions paginated (qanda forums)
723 */
724 public function test_mod_forum_get_forum_discussions_paginated_qanda() {
725
726 $this->resetAfterTest(true);
727
728 // Create courses to add the modules.
729 $course = self::getDataGenerator()->create_course();
730
731 $user1 = self::getDataGenerator()->create_user();
732 $user2 = self::getDataGenerator()->create_user();
733
734 // First forum with tracking off.
735 $record = new stdClass();
736 $record->course = $course->id;
737 $record->type = 'qanda';
738 $forum = self::getDataGenerator()->create_module('forum', $record);
739
740 // Add discussions to the forums.
741 $discussionrecord = new stdClass();
742 $discussionrecord->course = $course->id;
743 $discussionrecord->userid = $user2->id;
744 $discussionrecord->forum = $forum->id;
745 $discussion = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($discussionrecord);
746
747 self::setAdminUser();
748 $discussions = mod_forum_external::get_forum_discussions_paginated($forum->id);
749 $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_paginated_returns(), $discussions);
750
751 $this->assertCount(1, $discussions['discussions']);
752 $this->assertCount(0, $discussions['warnings']);
753
754 self::setUser($user1);
755 $this->getDataGenerator()->enrol_user($user1->id, $course->id);
756
757 $discussions = mod_forum_external::get_forum_discussions_paginated($forum->id);
758 $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_paginated_returns(), $discussions);
759
760 $this->assertCount(1, $discussions['discussions']);
761 $this->assertCount(0, $discussions['warnings']);
762
763 }
50a20317
JL
764
765 /**
766 * Test add_discussion_post
767 */
768 public function test_add_discussion_post() {
e881c4f5 769 global $CFG;
50a20317
JL
770
771 $this->resetAfterTest(true);
772
773 $user = self::getDataGenerator()->create_user();
774 $otheruser = self::getDataGenerator()->create_user();
775
776 self::setAdminUser();
777
778 // Create course to add the module.
779 $course = self::getDataGenerator()->create_course(array('groupmode' => VISIBLEGROUPS, 'groupmodeforce' => 0));
780
781 // Forum with tracking off.
782 $record = new stdClass();
783 $record->course = $course->id;
784 $forum = self::getDataGenerator()->create_module('forum', $record);
785 $cm = get_coursemodule_from_id('forum', $forum->cmid, 0, false, MUST_EXIST);
786 $forumcontext = context_module::instance($forum->cmid);
787
788 // Add discussions to the forums.
789 $record = new stdClass();
790 $record->course = $course->id;
791 $record->userid = $user->id;
792 $record->forum = $forum->id;
793 $discussion = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
794
795 // Try to post (user not enrolled).
796 self::setUser($user);
797 try {
798 mod_forum_external::add_discussion_post($discussion->firstpost, 'some subject', 'some text here...');
799 $this->fail('Exception expected due to being unenrolled from the course.');
800 } catch (moodle_exception $e) {
801 $this->assertEquals('requireloginerror', $e->errorcode);
802 }
803
804 $this->getDataGenerator()->enrol_user($user->id, $course->id);
805 $this->getDataGenerator()->enrol_user($otheruser->id, $course->id);
806
41182118
BK
807 $createdpost = mod_forum_external::add_discussion_post($discussion->firstpost, 'some subject', 'some text here...');
808 $createdpost = external_api::clean_returnvalue(mod_forum_external::add_discussion_post_returns(), $createdpost);
50a20317
JL
809
810 $posts = mod_forum_external::get_forum_discussion_posts($discussion->id);
811 $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts);
812 // We receive the discussion and the post.
813 $this->assertEquals(2, count($posts['posts']));
43ff833d
JL
814
815 $tested = false;
41182118
BK
816 foreach ($posts['posts'] as $thispost) {
817 if ($createdpost['postid'] == $thispost['id']) {
818 $this->assertEquals('some subject', $thispost['subject']);
819 $this->assertEquals('some text here...', $thispost['message']);
43ff833d
JL
820 $tested = true;
821 }
822 }
823 $this->assertTrue($tested);
50a20317 824
e881c4f5 825 // Test inline and regular attachment in post
41182118
BK
826 // Create a file in a draft area for inline attachments.
827 $draftidinlineattach = file_get_unused_draft_itemid();
e881c4f5 828 $draftidattach = file_get_unused_draft_itemid();
41182118
BK
829 self::setUser($user);
830 $usercontext = context_user::instance($user->id);
831 $filepath = '/';
832 $filearea = 'draft';
833 $component = 'user';
834 $filenameimg = 'shouldbeanimage.txt';
e881c4f5 835 $filerecordinline = array(
41182118
BK
836 'contextid' => $usercontext->id,
837 'component' => $component,
838 'filearea' => $filearea,
839 'itemid' => $draftidinlineattach,
840 'filepath' => $filepath,
841 'filename' => $filenameimg,
842 );
843 $fs = get_file_storage();
41182118 844
e881c4f5
BK
845 // Create a file in a draft area for regular attachments.
846 $filerecordattach = $filerecordinline;
847 $attachfilename = 'attachment.txt';
848 $filerecordattach['filename'] = $attachfilename;
849 $filerecordattach['itemid'] = $draftidattach;
850 $fs->create_file_from_string($filerecordinline, 'image contents (not really)');
851 $fs->create_file_from_string($filerecordattach, 'simple text attachment');
852
48143990 853 $options = array(array('name' => 'inlineattachmentsid', 'value' => $draftidinlineattach),
e881c4f5 854 array('name' => 'attachmentsid', 'value' => $draftidattach));
41182118
BK
855 $dummytext = 'Here is an inline image: <img src="' . $CFG->wwwroot
856 . "/draftfile.php/{$usercontext->id}/user/draft/{$draftidinlineattach}/{$filenameimg}"
857 . '" alt="inlineimage">.';
858 $createdpost = mod_forum_external::add_discussion_post($discussion->firstpost, 'new post inline attachment',
859 $dummytext, $options);
860 $createdpost = external_api::clean_returnvalue(mod_forum_external::add_discussion_post_returns(), $createdpost);
861
862 $posts = mod_forum_external::get_forum_discussion_posts($discussion->id);
863 $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts);
864 // We receive the discussion and the post.
865 // Can't guarantee order of posts during tests.
866 $postfound = false;
867 foreach ($posts['posts'] as $thispost) {
868 if ($createdpost['postid'] == $thispost['id']) {
869 $this->assertEquals($createdpost['postid'], $thispost['id']);
e881c4f5
BK
870 $this->assertEquals($thispost['attachment'], 1, "There should be a non-inline attachment");
871 $this->assertCount(1, $thispost['attachments'], "There should be 1 attachment");
872 $this->assertEquals($thispost['attachments'][0]['filename'], $attachfilename, "There should be 1 attachment");
41182118
BK
873 $this->assertContains('pluginfile.php', $thispost['message']);
874 $postfound = true;
e881c4f5 875 break;
41182118
BK
876 }
877 }
878
879 $this->assertTrue($postfound);
880
50a20317
JL
881 // Check not posting in groups the user is not member of.
882 $group = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
883 groups_add_member($group->id, $otheruser->id);
884
885 $forum = self::getDataGenerator()->create_module('forum', $record, array('groupmode' => SEPARATEGROUPS));
886 $record->forum = $forum->id;
887 $record->userid = $otheruser->id;
888 $record->groupid = $group->id;
889 $discussion = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
890
891 try {
892 mod_forum_external::add_discussion_post($discussion->firstpost, 'some subject', 'some text here...');
893 $this->fail('Exception expected due to invalid permissions for posting.');
894 } catch (moodle_exception $e) {
50a20317
JL
895 $this->assertEquals('nopostforum', $e->errorcode);
896 }
897
898 }
7ab43ac8
JL
899
900 /*
901 * Test add_discussion. A basic test since all the API functions are already covered by unit tests.
902 */
903 public function test_add_discussion() {
41182118 904 global $CFG, $USER;
7ab43ac8
JL
905 $this->resetAfterTest(true);
906
907 // Create courses to add the modules.
908 $course = self::getDataGenerator()->create_course();
909
910 $user1 = self::getDataGenerator()->create_user();
911 $user2 = self::getDataGenerator()->create_user();
912
913 // First forum with tracking off.
914 $record = new stdClass();
915 $record->course = $course->id;
916 $record->type = 'news';
917 $forum = self::getDataGenerator()->create_module('forum', $record);
918
919 self::setUser($user1);
920 $this->getDataGenerator()->enrol_user($user1->id, $course->id);
921
922 try {
923 mod_forum_external::add_discussion($forum->id, 'the subject', 'some text here...');
924 $this->fail('Exception expected due to invalid permissions.');
925 } catch (moodle_exception $e) {
926 $this->assertEquals('cannotcreatediscussion', $e->errorcode);
927 }
928
929 self::setAdminUser();
41182118
BK
930 $createddiscussion = mod_forum_external::add_discussion($forum->id, 'the subject', 'some text here...');
931 $createddiscussion = external_api::clean_returnvalue(mod_forum_external::add_discussion_returns(), $createddiscussion);
7ab43ac8
JL
932
933 $discussions = mod_forum_external::get_forum_discussions_paginated($forum->id);
934 $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_paginated_returns(), $discussions);
935
936 $this->assertCount(1, $discussions['discussions']);
937 $this->assertCount(0, $discussions['warnings']);
938
41182118 939 $this->assertEquals($createddiscussion['discussionid'], $discussions['discussions'][0]['discussion']);
7ab43ac8
JL
940 $this->assertEquals(-1, $discussions['discussions'][0]['groupid']);
941 $this->assertEquals('the subject', $discussions['discussions'][0]['subject']);
942 $this->assertEquals('some text here...', $discussions['discussions'][0]['message']);
943
5f219cf1
BK
944 $discussion2pinned = mod_forum_external::add_discussion($forum->id, 'the pinned subject', 'some 2 text here...', -1,
945 array('options' => array('name' => 'discussionpinned',
946 'value' => true)));
947 $discussion3 = mod_forum_external::add_discussion($forum->id, 'the non pinnedsubject', 'some 3 text here...');
948 $discussions = mod_forum_external::get_forum_discussions_paginated($forum->id);
949 $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_paginated_returns(), $discussions);
950 $this->assertCount(3, $discussions['discussions']);
951 $this->assertEquals($discussion2pinned['discussionid'], $discussions['discussions'][0]['discussion']);
41182118 952
e881c4f5 953 // Test inline and regular attachment in new discussion
41182118 954 // Create a file in a draft area for inline attachments.
e881c4f5
BK
955
956 $fs = get_file_storage();
957
41182118 958 $draftidinlineattach = file_get_unused_draft_itemid();
e881c4f5
BK
959 $draftidattach = file_get_unused_draft_itemid();
960
41182118
BK
961 $usercontext = context_user::instance($USER->id);
962 $filepath = '/';
963 $filearea = 'draft';
964 $component = 'user';
965 $filenameimg = 'shouldbeanimage.txt';
966 $filerecord = array(
967 'contextid' => $usercontext->id,
968 'component' => $component,
969 'filearea' => $filearea,
970 'itemid' => $draftidinlineattach,
971 'filepath' => $filepath,
972 'filename' => $filenameimg,
973 );
e881c4f5 974
e881c4f5
BK
975 // Create a file in a draft area for regular attachments.
976 $filerecordattach = $filerecord;
977 $attachfilename = 'attachment.txt';
978 $filerecordattach['filename'] = $attachfilename;
979 $filerecordattach['itemid'] = $draftidattach;
41182118 980 $fs->create_file_from_string($filerecord, 'image contents (not really)');
e881c4f5
BK
981 $fs->create_file_from_string($filerecordattach, 'simple text attachment');
982
41182118
BK
983 $dummytext = 'Here is an inline image: <img src="' . $CFG->wwwroot .
984 "/draftfile.php/{$usercontext->id}/user/draft/{$draftidinlineattach}/{$filenameimg}" .
985 '" alt="inlineimage">.';
986
48143990 987 $options = array(array('name' => 'inlineattachmentsid', 'value' => $draftidinlineattach),
e881c4f5 988 array('name' => 'attachmentsid', 'value' => $draftidattach));
41182118
BK
989 $createddiscussion = mod_forum_external::add_discussion($forum->id, 'the inline attachment subject',
990 $dummytext, -1, $options);
991 $createddiscussion = external_api::clean_returnvalue(mod_forum_external::add_discussion_returns(), $createddiscussion);
992
993 $discussions = mod_forum_external::get_forum_discussions_paginated($forum->id);
994 $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_paginated_returns(), $discussions);
995
996 $this->assertCount(4, $discussions['discussions']);
997 $this->assertCount(0, $createddiscussion['warnings']);
998 // Can't guarantee order of posts during tests.
999 $postfound = false;
1000 foreach ($discussions['discussions'] as $thisdiscussion) {
1001 if ($createddiscussion['discussionid'] == $thisdiscussion['discussion']) {
e881c4f5
BK
1002 $this->assertEquals($thisdiscussion['attachment'], 1, "There should be a non-inline attachment");
1003 $this->assertCount(1, $thisdiscussion['attachments'], "There should be 1 attachment");
1004 $this->assertEquals($thisdiscussion['attachments'][0]['filename'], $attachfilename, "There should be 1 attachment");
41182118
BK
1005 $this->assertNotContains('draftfile.php', $thisdiscussion['message']);
1006 $this->assertContains('pluginfile.php', $thisdiscussion['message']);
1007 $postfound = true;
e881c4f5 1008 break;
41182118
BK
1009 }
1010 }
1011
1012 $this->assertTrue($postfound);
7ab43ac8
JL
1013 }
1014
1015 /**
1016 * Test adding discussions in a course with gorups
1017 */
1018 public function test_add_discussion_in_course_with_groups() {
1019 global $CFG;
1020
1021 $this->resetAfterTest(true);
1022
1023 // Create course to add the module.
1024 $course = self::getDataGenerator()->create_course(array('groupmode' => VISIBLEGROUPS, 'groupmodeforce' => 0));
1025 $user = self::getDataGenerator()->create_user();
1026 $this->getDataGenerator()->enrol_user($user->id, $course->id);
1027
1028 // Forum forcing separate gropus.
1029 $record = new stdClass();
1030 $record->course = $course->id;
1031 $forum = self::getDataGenerator()->create_module('forum', $record, array('groupmode' => SEPARATEGROUPS));
1032
1033 // Try to post (user not enrolled).
1034 self::setUser($user);
1035
1036 // The user is not enroled in any group, try to post in a forum with separate groups.
1037 try {
1038 mod_forum_external::add_discussion($forum->id, 'the subject', 'some text here...');
1039 $this->fail('Exception expected due to invalid group permissions.');
1040 } catch (moodle_exception $e) {
1041 $this->assertEquals('cannotcreatediscussion', $e->errorcode);
1042 }
1043
1044 try {
1045 mod_forum_external::add_discussion($forum->id, 'the subject', 'some text here...', 0);
1046 $this->fail('Exception expected due to invalid group permissions.');
1047 } catch (moodle_exception $e) {
1048 $this->assertEquals('cannotcreatediscussion', $e->errorcode);
1049 }
1050
1051 // Create a group.
1052 $group = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
1053
1054 // Try to post in a group the user is not enrolled.
1055 try {
1056 mod_forum_external::add_discussion($forum->id, 'the subject', 'some text here...', $group->id);
1057 $this->fail('Exception expected due to invalid group permissions.');
1058 } catch (moodle_exception $e) {
1059 $this->assertEquals('cannotcreatediscussion', $e->errorcode);
1060 }
1061
1062 // Add the user to a group.
1063 groups_add_member($group->id, $user->id);
1064
1065 // Try to post in a group the user is not enrolled.
1066 try {
1067 mod_forum_external::add_discussion($forum->id, 'the subject', 'some text here...', $group->id + 1);
1068 $this->fail('Exception expected due to invalid group.');
1069 } catch (moodle_exception $e) {
1070 $this->assertEquals('cannotcreatediscussion', $e->errorcode);
1071 }
1072
1073 // Nost add the discussion using a valid group.
1074 $discussion = mod_forum_external::add_discussion($forum->id, 'the subject', 'some text here...', $group->id);
1075 $discussion = external_api::clean_returnvalue(mod_forum_external::add_discussion_returns(), $discussion);
1076
1077 $discussions = mod_forum_external::get_forum_discussions_paginated($forum->id);
1078 $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_paginated_returns(), $discussions);
1079
1080 $this->assertCount(1, $discussions['discussions']);
1081 $this->assertCount(0, $discussions['warnings']);
1082 $this->assertEquals($discussion['discussionid'], $discussions['discussions'][0]['discussion']);
1083 $this->assertEquals($group->id, $discussions['discussions'][0]['groupid']);
1084
1085 // Now add a discussions without indicating a group. The function should guess the correct group.
1086 $discussion = mod_forum_external::add_discussion($forum->id, 'the subject', 'some text here...');
1087 $discussion = external_api::clean_returnvalue(mod_forum_external::add_discussion_returns(), $discussion);
1088
1089 $discussions = mod_forum_external::get_forum_discussions_paginated($forum->id);
1090 $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_paginated_returns(), $discussions);
1091
1092 $this->assertCount(2, $discussions['discussions']);
1093 $this->assertCount(0, $discussions['warnings']);
1094 $this->assertEquals($group->id, $discussions['discussions'][0]['groupid']);
1095 $this->assertEquals($group->id, $discussions['discussions'][1]['groupid']);
1096
1097 // Enrol the same user in other group.
1098 $group2 = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
1099 groups_add_member($group2->id, $user->id);
1100
1101 // Now add a discussions without indicating a group. The function should guess the correct group (the first one).
1102 $discussion = mod_forum_external::add_discussion($forum->id, 'the subject', 'some text here...');
1103 $discussion = external_api::clean_returnvalue(mod_forum_external::add_discussion_returns(), $discussion);
1104
1105 $discussions = mod_forum_external::get_forum_discussions_paginated($forum->id);
1106 $discussions = external_api::clean_returnvalue(mod_forum_external::get_forum_discussions_paginated_returns(), $discussions);
1107
1108 $this->assertCount(3, $discussions['discussions']);
1109 $this->assertCount(0, $discussions['warnings']);
1110 $this->assertEquals($group->id, $discussions['discussions'][0]['groupid']);
1111 $this->assertEquals($group->id, $discussions['discussions'][1]['groupid']);
1112 $this->assertEquals($group->id, $discussions['discussions'][2]['groupid']);
1113
1114 }
1115
04cd8ae3
JL
1116 /*
1117 * Test can_add_discussion. A basic test since all the API functions are already covered by unit tests.
1118 */
1119 public function test_can_add_discussion() {
581e75bf 1120 global $DB;
04cd8ae3
JL
1121 $this->resetAfterTest(true);
1122
1123 // Create courses to add the modules.
1124 $course = self::getDataGenerator()->create_course();
1125
1126 $user = self::getDataGenerator()->create_user();
1127
1128 // First forum with tracking off.
1129 $record = new stdClass();
1130 $record->course = $course->id;
1131 $record->type = 'news';
1132 $forum = self::getDataGenerator()->create_module('forum', $record);
1133
1134 // User with no permissions to add in a news forum.
1135 self::setUser($user);
1136 $this->getDataGenerator()->enrol_user($user->id, $course->id);
1137
1138 $result = mod_forum_external::can_add_discussion($forum->id);
1139 $result = external_api::clean_returnvalue(mod_forum_external::can_add_discussion_returns(), $result);
1140 $this->assertFalse($result['status']);
581e75bf
JL
1141 $this->assertFalse($result['canpindiscussions']);
1142 $this->assertTrue($result['cancreateattachment']);
1143
1144 // Disable attachments.
1145 $DB->set_field('forum', 'maxattachments', 0, array('id' => $forum->id));
1146 $result = mod_forum_external::can_add_discussion($forum->id);
1147 $result = external_api::clean_returnvalue(mod_forum_external::can_add_discussion_returns(), $result);
1148 $this->assertFalse($result['status']);
1149 $this->assertFalse($result['canpindiscussions']);
1150 $this->assertFalse($result['cancreateattachment']);
1151 $DB->set_field('forum', 'maxattachments', 1, array('id' => $forum->id)); // Enable attachments again.
04cd8ae3
JL
1152
1153 self::setAdminUser();
1154 $result = mod_forum_external::can_add_discussion($forum->id);
1155 $result = external_api::clean_returnvalue(mod_forum_external::can_add_discussion_returns(), $result);
1156 $this->assertTrue($result['status']);
581e75bf
JL
1157 $this->assertTrue($result['canpindiscussions']);
1158 $this->assertTrue($result['cancreateattachment']);
04cd8ae3
JL
1159
1160 }
1161
b7ce46df
JL
1162 /**
1163 * Test get forum posts discussions including rating information.
1164 */
1165 public function test_mod_forum_get_forum_discussion_rating_information() {
1166 global $DB, $CFG;
1167 require_once($CFG->dirroot . '/rating/lib.php');
1168
1169 $this->resetAfterTest(true);
1170
1171 $user1 = self::getDataGenerator()->create_user();
1172 $user2 = self::getDataGenerator()->create_user();
1173 $user3 = self::getDataGenerator()->create_user();
1174 $teacher = self::getDataGenerator()->create_user();
1175
1176 // Create course to add the module.
1177 $course = self::getDataGenerator()->create_course();
1178
1179 $studentrole = $DB->get_record('role', array('shortname' => 'student'));
1180 $teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'));
1181 $this->getDataGenerator()->enrol_user($user1->id, $course->id, $studentrole->id, 'manual');
1182 $this->getDataGenerator()->enrol_user($user2->id, $course->id, $studentrole->id, 'manual');
1183 $this->getDataGenerator()->enrol_user($user3->id, $course->id, $studentrole->id, 'manual');
1184 $this->getDataGenerator()->enrol_user($teacher->id, $course->id, $teacherrole->id, 'manual');
1185
1186 // Create the forum.
1187 $record = new stdClass();
1188 $record->course = $course->id;
1189 // Set Aggregate type = Average of ratings.
1190 $record->assessed = RATING_AGGREGATE_AVERAGE;
1191 $record->scale = 100;
1192 $forum = self::getDataGenerator()->create_module('forum', $record);
1193 $context = context_module::instance($forum->cmid);
1194
1195 // Add discussion to the forum.
1196 $record = new stdClass();
1197 $record->course = $course->id;
1198 $record->userid = $user1->id;
1199 $record->forum = $forum->id;
1200 $discussion = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
1201
1202 // Retrieve the first post.
1203 $post = $DB->get_record('forum_posts', array('discussion' => $discussion->id));
1204
1205 // Rate the discussion as user2.
1206 $rating1 = new stdClass();
1207 $rating1->contextid = $context->id;
1208 $rating1->component = 'mod_forum';
1209 $rating1->ratingarea = 'post';
1210 $rating1->itemid = $post->id;
1211 $rating1->rating = 50;
1212 $rating1->scaleid = 100;
1213 $rating1->userid = $user2->id;
1214 $rating1->timecreated = time();
1215 $rating1->timemodified = time();
1216 $rating1->id = $DB->insert_record('rating', $rating1);
1217
1218 // Rate the discussion as user3.
1219 $rating2 = new stdClass();
1220 $rating2->contextid = $context->id;
1221 $rating2->component = 'mod_forum';
1222 $rating2->ratingarea = 'post';
1223 $rating2->itemid = $post->id;
1224 $rating2->rating = 100;
1225 $rating2->scaleid = 100;
1226 $rating2->userid = $user3->id;
1227 $rating2->timecreated = time() + 1;
1228 $rating2->timemodified = time() + 1;
1229 $rating2->id = $DB->insert_record('rating', $rating2);
1230
1231 // Retrieve the rating for the post as student.
1232 $this->setUser($user1);
1233 $posts = mod_forum_external::get_forum_discussion_posts($discussion->id);
1234 $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts);
1235 $this->assertCount(1, $posts['ratinginfo']['ratings']);
1236 $this->assertTrue($posts['ratinginfo']['ratings'][0]['canviewaggregate']);
1237 $this->assertFalse($posts['ratinginfo']['canviewall']);
1238 $this->assertFalse($posts['ratinginfo']['ratings'][0]['canrate']);
1239 $this->assertEquals(2, $posts['ratinginfo']['ratings'][0]['count']);
1240 $this->assertEquals(($rating1->rating + $rating2->rating) / 2, $posts['ratinginfo']['ratings'][0]['aggregate']);
1241
1242 // Retrieve the rating for the post as teacher.
1243 $this->setUser($teacher);
1244 $posts = mod_forum_external::get_forum_discussion_posts($discussion->id);
1245 $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts);
1246 $this->assertCount(1, $posts['ratinginfo']['ratings']);
1247 $this->assertTrue($posts['ratinginfo']['ratings'][0]['canviewaggregate']);
1248 $this->assertTrue($posts['ratinginfo']['canviewall']);
1249 $this->assertTrue($posts['ratinginfo']['ratings'][0]['canrate']);
1250 $this->assertEquals(2, $posts['ratinginfo']['ratings'][0]['count']);
1251 $this->assertEquals(($rating1->rating + $rating2->rating) / 2, $posts['ratinginfo']['ratings'][0]['aggregate']);
1252 }
4daa0d08
JL
1253
1254 /**
1255 * Test mod_forum_get_forum_access_information.
1256 */
1257 public function test_mod_forum_get_forum_access_information() {
1258 global $DB;
1259
1260 $this->resetAfterTest(true);
1261
1262 $student = self::getDataGenerator()->create_user();
1263 $course = self::getDataGenerator()->create_course();
1264 // Create the forum.
1265 $record = new stdClass();
1266 $record->course = $course->id;
1267 $forum = self::getDataGenerator()->create_module('forum', $record);
1268
1269 $studentrole = $DB->get_record('role', array('shortname' => 'student'));
1270 $this->getDataGenerator()->enrol_user($student->id, $course->id, $studentrole->id, 'manual');
1271
1272 self::setUser($student);
1273 $result = mod_forum_external::get_forum_access_information($forum->id);
1274 $result = external_api::clean_returnvalue(mod_forum_external::get_forum_access_information_returns(), $result);
1275
1276 // Check default values for capabilities.
1277 $enabledcaps = array('canviewdiscussion', 'canstartdiscussion', 'canreplypost', 'canviewrating', 'cancreateattachment',
1278 'canexportownpost', 'candeleteownpost', 'canallowforcesubscribe');
1279
1280 unset($result['warnings']);
1281 foreach ($result as $capname => $capvalue) {
1282 if (in_array($capname, $enabledcaps)) {
1283 $this->assertTrue($capvalue);
1284 } else {
1285 $this->assertFalse($capvalue);
1286 }
1287 }
1288 // Now, unassign some capabilities.
1289 unassign_capability('mod/forum:deleteownpost', $studentrole->id);
1290 unassign_capability('mod/forum:allowforcesubscribe', $studentrole->id);
1291 array_pop($enabledcaps);
1292 array_pop($enabledcaps);
1293 accesslib_clear_all_caches_for_unit_testing();
1294
1295 $result = mod_forum_external::get_forum_access_information($forum->id);
1296 $result = external_api::clean_returnvalue(mod_forum_external::get_forum_access_information_returns(), $result);
1297 unset($result['warnings']);
1298 foreach ($result as $capname => $capvalue) {
1299 if (in_array($capname, $enabledcaps)) {
1300 $this->assertTrue($capvalue);
1301 } else {
1302 $this->assertFalse($capvalue);
1303 }
1304 }
1305 }
bc4c7337
AN
1306
1307 /**
1308 * Test add_discussion_post
1309 */
1310 public function test_add_discussion_post_private() {
1311 global $DB;
1312
1313 $this->resetAfterTest(true);
1314
1315 self::setAdminUser();
1316
1317 // Create course to add the module.
1318 $course = self::getDataGenerator()->create_course();
1319
1320 // Standard forum.
1321 $record = new stdClass();
1322 $record->course = $course->id;
1323 $forum = self::getDataGenerator()->create_module('forum', $record);
1324 $cm = get_coursemodule_from_id('forum', $forum->cmid, 0, false, MUST_EXIST);
1325 $forumcontext = context_module::instance($forum->cmid);
1326 $generator = self::getDataGenerator()->get_plugin_generator('mod_forum');
1327
1328 // Create an enrol users.
1329 $student1 = self::getDataGenerator()->create_user();
1330 $this->getDataGenerator()->enrol_user($student1->id, $course->id, 'student');
1331 $student2 = self::getDataGenerator()->create_user();
1332 $this->getDataGenerator()->enrol_user($student2->id, $course->id, 'student');
1333 $teacher1 = self::getDataGenerator()->create_user();
1334 $this->getDataGenerator()->enrol_user($teacher1->id, $course->id, 'editingteacher');
1335 $teacher2 = self::getDataGenerator()->create_user();
1336 $this->getDataGenerator()->enrol_user($teacher2->id, $course->id, 'editingteacher');
1337
1338 // Add a new discussion to the forum.
1339 self::setUser($student1);
1340 $record = new stdClass();
1341 $record->course = $course->id;
1342 $record->userid = $student1->id;
1343 $record->forum = $forum->id;
1344 $discussion = $generator->create_discussion($record);
1345
1346 // Have the teacher reply privately.
1347 self::setUser($teacher1);
1348 $post = mod_forum_external::add_discussion_post($discussion->firstpost, 'some subject', 'some text here...', [
1349 [
1350 'name' => 'private',
1351 'value' => true,
1352 ],
1353 ]);
1354 $post = external_api::clean_returnvalue(mod_forum_external::add_discussion_post_returns(), $post);
1355 $privatereply = $DB->get_record('forum_posts', array('id' => $post['postid']));
1356 $this->assertEquals($student1->id, $privatereply->privatereplyto);
1357 // Bump the time of the private reply to ensure order.
1358 $privatereply->created++;
1359 $privatereply->modified = $privatereply->created;
1360 $DB->update_record('forum_posts', $privatereply);
1361
1362 // The teacher will receive their private reply.
1363 self::setUser($teacher1);
1364 $posts = mod_forum_external::get_forum_discussion_posts($discussion->id);
1365 $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts);
1366 $this->assertEquals(2, count($posts['posts']));
1367 $this->assertTrue($posts['posts'][0]['isprivatereply']);
1368
1369 // Another teacher on the course will also receive the private reply.
1370 self::setUser($teacher2);
1371 $posts = mod_forum_external::get_forum_discussion_posts($discussion->id);
1372 $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts);
1373 $this->assertEquals(2, count($posts['posts']));
1374 $this->assertTrue($posts['posts'][0]['isprivatereply']);
1375
1376 // The student will receive the private reply.
1377 self::setUser($student1);
1378 $posts = mod_forum_external::get_forum_discussion_posts($discussion->id);
1379 $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts);
1380 $this->assertEquals(2, count($posts['posts']));
1381 $this->assertTrue($posts['posts'][0]['isprivatereply']);
1382
1383 // Another student will not receive the private reply.
1384 self::setUser($student2);
1385 $posts = mod_forum_external::get_forum_discussion_posts($discussion->id);
1386 $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts);
1387 $this->assertEquals(1, count($posts['posts']));
1388 $this->assertFalse($posts['posts'][0]['isprivatereply']);
1389
1390 // A user cannot reply to a private reply.
1391 self::setUser($teacher2);
1392 $this->expectException('coding_exception');
1393 $post = mod_forum_external::add_discussion_post($privatereply->id, 'some subject', 'some text here...', [
1394 'options' => [
1395 'name' => 'private',
1396 'value' => false,
1397 ],
1398 ]);
1399 }
2b9fe87d 1400}