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