MDL-59246 mod_workshop: New WS get_submission_assessments
[moodle.git] / mod / workshop / tests / external_test.php
CommitLineData
9f1ab2db
JL
1<?php
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 * Workshop module external functions tests
19 *
20 * @package mod_workshop
21 * @category external
22 * @copyright 2017 Juan Leyva <juan@moodle.com>
23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24 * @since Moodle 3.4
25 */
26
27defined('MOODLE_INTERNAL') || die();
28
29global $CFG;
30
31require_once($CFG->dirroot . '/webservice/tests/helpers.php');
32require_once($CFG->dirroot . '/mod/workshop/lib.php');
33
34use mod_workshop\external\workshop_summary_exporter;
3f08cfc5 35use mod_workshop\external\submission_exporter;
9f1ab2db
JL
36
37/**
38 * Workshop module external functions tests
39 *
40 * @package mod_workshop
41 * @category external
42 * @copyright 2017 Juan Leyva <juan@moodle.com>
43 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
44 * @since Moodle 3.4
45 */
46class mod_workshop_external_testcase extends externallib_advanced_testcase {
47
48 /** @var stdClass course object */
49 private $course;
50 /** @var stdClass workshop object */
51 private $workshop;
52 /** @var stdClass context object */
53 private $context;
54 /** @var stdClass cm object */
55 private $cm;
56 /** @var stdClass student object */
57 private $student;
58 /** @var stdClass teacher object */
59 private $teacher;
60 /** @var stdClass student role object */
61 private $studentrole;
62 /** @var stdClass teacher role object */
63 private $teacherrole;
64
65 /**
66 * Set up for every test
67 */
68 public function setUp() {
69 global $DB;
70 $this->resetAfterTest();
71 $this->setAdminUser();
72
73 // Setup test data.
3f08cfc5
JL
74 $course = new stdClass();
75 $course->groupmode = SEPARATEGROUPS;
76 $course->groupmodeforce = true;
77 $this->course = $this->getDataGenerator()->create_course($course);
9f1ab2db
JL
78 $this->workshop = $this->getDataGenerator()->create_module('workshop', array('course' => $this->course->id));
79 $this->context = context_module::instance($this->workshop->cmid);
80 $this->cm = get_coursemodule_from_instance('workshop', $this->workshop->id);
81
82 // Create users.
83 $this->student = self::getDataGenerator()->create_user();
3f08cfc5
JL
84 $this->anotherstudentg1 = self::getDataGenerator()->create_user();
85 $this->anotherstudentg2 = self::getDataGenerator()->create_user();
9f1ab2db
JL
86 $this->teacher = self::getDataGenerator()->create_user();
87
88 // Users enrolments.
89 $this->studentrole = $DB->get_record('role', array('shortname' => 'student'));
90 $this->teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'));
91 $this->getDataGenerator()->enrol_user($this->student->id, $this->course->id, $this->studentrole->id, 'manual');
3f08cfc5
JL
92 $this->getDataGenerator()->enrol_user($this->anotherstudentg1->id, $this->course->id, $this->studentrole->id, 'manual');
93 $this->getDataGenerator()->enrol_user($this->anotherstudentg2->id, $this->course->id, $this->studentrole->id, 'manual');
9f1ab2db 94 $this->getDataGenerator()->enrol_user($this->teacher->id, $this->course->id, $this->teacherrole->id, 'manual');
3f08cfc5
JL
95
96 $this->group1 = $this->getDataGenerator()->create_group(array('courseid' => $this->course->id));
97 $this->group2 = $this->getDataGenerator()->create_group(array('courseid' => $this->course->id));
98 groups_add_member($this->group1, $this->student);
99 groups_add_member($this->group1, $this->anotherstudentg1);
100 groups_add_member($this->group2, $this->anotherstudentg2);
9f1ab2db
JL
101 }
102
103 /**
104 * Test test_mod_workshop_get_workshops_by_courses
105 */
106 public function test_mod_workshop_get_workshops_by_courses() {
107 global $DB;
108
109 // Create additional course.
110 $course2 = self::getDataGenerator()->create_course();
111
112 // Second workshop.
113 $record = new stdClass();
114 $record->course = $course2->id;
115 $workshop2 = self::getDataGenerator()->create_module('workshop', $record);
116
117 // Execute real Moodle enrolment as we'll call unenrol() method on the instance later.
118 $enrol = enrol_get_plugin('manual');
119 $enrolinstances = enrol_get_instances($course2->id, true);
120 foreach ($enrolinstances as $courseenrolinstance) {
121 if ($courseenrolinstance->enrol == "manual") {
122 $instance2 = $courseenrolinstance;
123 break;
124 }
125 }
126 $enrol->enrol_user($instance2, $this->student->id, $this->studentrole->id);
127
128 self::setUser($this->student);
129
130 $returndescription = mod_workshop_external::get_workshops_by_courses_returns();
131
132 // Create what we expect to be returned when querying the two courses.
133 $properties = workshop_summary_exporter::read_properties_definition();
134 $expectedfields = array_keys($properties);
135
136 // Add expected coursemodule and data.
137 $workshop1 = $this->workshop;
138 $workshop1->coursemodule = $workshop1->cmid;
139 $workshop1->introformat = 1;
140 $workshop1->introfiles = [];
141 $workshop1->instructauthorsfiles = [];
142 $workshop1->instructauthorsformat = 1;
143 $workshop1->instructreviewersfiles = [];
144 $workshop1->instructreviewersformat = 1;
145 $workshop1->conclusionfiles = [];
146 $workshop1->conclusionformat = 1;
147
148 $workshop2->coursemodule = $workshop2->cmid;
149 $workshop2->introformat = 1;
150 $workshop2->introfiles = [];
151 $workshop2->instructauthorsfiles = [];
152 $workshop2->instructauthorsformat = 1;
153 $workshop2->instructreviewersfiles = [];
154 $workshop2->instructreviewersformat = 1;
155 $workshop2->conclusionfiles = [];
156 $workshop2->conclusionformat = 1;
157
158 foreach ($expectedfields as $field) {
159 if (!empty($properties[$field]) && $properties[$field]['type'] == PARAM_BOOL) {
160 $workshop1->{$field} = (bool) $workshop1->{$field};
161 $workshop2->{$field} = (bool) $workshop2->{$field};
162 }
163 $expected1[$field] = $workshop1->{$field};
164 $expected2[$field] = $workshop2->{$field};
165 }
166
167 $expectedworkshops = array($expected2, $expected1);
168
169 // Call the external function passing course ids.
170 $result = mod_workshop_external::get_workshops_by_courses(array($course2->id, $this->course->id));
171 $result = external_api::clean_returnvalue($returndescription, $result);
172
173 $this->assertEquals($expectedworkshops, $result['workshops']);
174 $this->assertCount(0, $result['warnings']);
175
176 // Call the external function without passing course id.
177 $result = mod_workshop_external::get_workshops_by_courses();
178 $result = external_api::clean_returnvalue($returndescription, $result);
179 $this->assertEquals($expectedworkshops, $result['workshops']);
180 $this->assertCount(0, $result['warnings']);
181
182 // Unenrol user from second course and alter expected workshops.
183 $enrol->unenrol_user($instance2, $this->student->id);
184 array_shift($expectedworkshops);
185
186 // Call the external function without passing course id.
187 $result = mod_workshop_external::get_workshops_by_courses();
188 $result = external_api::clean_returnvalue($returndescription, $result);
189 $this->assertEquals($expectedworkshops, $result['workshops']);
190
191 // Call for the second course we unenrolled the user from, expected warning.
192 $result = mod_workshop_external::get_workshops_by_courses(array($course2->id));
193 $this->assertCount(1, $result['warnings']);
194 $this->assertEquals('1', $result['warnings'][0]['warningcode']);
195 $this->assertEquals($course2->id, $result['warnings'][0]['itemid']);
196 }
977fdfa3
JL
197
198 /**
199 * Test mod_workshop_get_workshop_access_information for students.
200 */
201 public function test_mod_workshop_get_workshop_access_information_student() {
202
203 self::setUser($this->student);
204 $result = mod_workshop_external::get_workshop_access_information($this->workshop->id);
205 $result = external_api::clean_returnvalue(mod_workshop_external::get_workshop_access_information_returns(), $result);
206 // Check default values for capabilities.
207 $enabledcaps = array('canpeerassess', 'cansubmit', 'canview', 'canviewauthornames', 'canviewauthorpublished',
208 'canviewpublishedsubmissions', 'canexportsubmissions');
209
210 foreach ($result as $capname => $capvalue) {
211 if (strpos($capname, 'can') !== 0) {
212 continue;
213 }
214 if (in_array($capname, $enabledcaps)) {
215 $this->assertTrue($capvalue);
216 } else {
217 $this->assertFalse($capvalue);
218 }
219 }
220 // Now, unassign some capabilities.
221 unassign_capability('mod/workshop:peerassess', $this->studentrole->id);
222 unassign_capability('mod/workshop:submit', $this->studentrole->id);
223 unset($enabledcaps[0]);
224 unset($enabledcaps[1]);
225 accesslib_clear_all_caches_for_unit_testing();
226
227 $result = mod_workshop_external::get_workshop_access_information($this->workshop->id);
228 $result = external_api::clean_returnvalue(mod_workshop_external::get_workshop_access_information_returns(), $result);
229 foreach ($result as $capname => $capvalue) {
230 if (strpos($capname, 'can') !== 0) {
231 continue;
232 }
233 if (in_array($capname, $enabledcaps)) {
234 $this->assertTrue($capvalue);
235 } else {
236 $this->assertFalse($capvalue);
237 }
238 }
239
240 // Now, specific functionalities.
241 $this->assertFalse($result['creatingsubmissionallowed']);
242 $this->assertFalse($result['modifyingsubmissionallowed']);
243 $this->assertFalse($result['assessingallowed']);
244 $this->assertFalse($result['assessingexamplesallowed']);
c2cf2450 245 $this->assertTrue($result['examplesassessed']);
977fdfa3
JL
246
247 // Switch phase.
248 $workshop = new workshop($this->workshop, $this->cm, $this->course);
249 $workshop->switch_phase(workshop::PHASE_SUBMISSION);
250 $result = mod_workshop_external::get_workshop_access_information($this->workshop->id);
251 $result = external_api::clean_returnvalue(mod_workshop_external::get_workshop_access_information_returns(), $result);
252
253 $this->assertTrue($result['creatingsubmissionallowed']);
254 $this->assertTrue($result['modifyingsubmissionallowed']);
255 $this->assertFalse($result['assessingallowed']);
256 $this->assertFalse($result['assessingexamplesallowed']);
c2cf2450 257 $this->assertTrue($result['examplesassessed']);
977fdfa3
JL
258
259 // Switch to next (to assessment).
260 $workshop = new workshop($this->workshop, $this->cm, $this->course);
261 $workshop->switch_phase(workshop::PHASE_ASSESSMENT);
262 $result = mod_workshop_external::get_workshop_access_information($this->workshop->id);
263 $result = external_api::clean_returnvalue(mod_workshop_external::get_workshop_access_information_returns(), $result);
264
265 $this->assertFalse($result['creatingsubmissionallowed']);
266 $this->assertFalse($result['modifyingsubmissionallowed']);
267 $this->assertTrue($result['assessingallowed']);
268 $this->assertFalse($result['assessingexamplesallowed']);
c2cf2450 269 $this->assertTrue($result['examplesassessed']);
977fdfa3
JL
270 }
271
272 /**
273 * Test mod_workshop_get_workshop_access_information for teachers.
274 */
275 public function test_mod_workshop_get_workshop_access_information_teacher() {
276
277 self::setUser($this->teacher);
278 $result = mod_workshop_external::get_workshop_access_information($this->workshop->id);
279 $result = external_api::clean_returnvalue(mod_workshop_external::get_workshop_access_information_returns(), $result);
280 // Check default values.
281 $disabledcaps = array('canpeerassess', 'cansubmit');
282
283 foreach ($result as $capname => $capvalue) {
284 if (strpos($capname, 'can') !== 0) {
285 continue;
286 }
287 if (in_array($capname, $disabledcaps)) {
288 $this->assertFalse($capvalue);
289 } else {
290 $this->assertTrue($capvalue);
291 }
292 }
293
294 // Now, specific functionalities.
295 $this->assertFalse($result['creatingsubmissionallowed']);
296 $this->assertFalse($result['modifyingsubmissionallowed']);
297 $this->assertFalse($result['assessingallowed']);
298 $this->assertFalse($result['assessingexamplesallowed']);
299 }
cd495029
JL
300
301 /**
302 * Test mod_workshop_get_user_plan for students.
303 */
304 public function test_mod_workshop_get_user_plan_student() {
305
306 self::setUser($this->student);
307 $result = mod_workshop_external::get_user_plan($this->workshop->id);
308 $result = external_api::clean_returnvalue(mod_workshop_external::get_user_plan_returns(), $result);
309
310 $this->assertCount(0, $result['userplan']['examples']); // No examples given.
311 $this->assertCount(5, $result['userplan']['phases']); // Always 5 phases.
312 $this->assertEquals(workshop::PHASE_SETUP, $result['userplan']['phases'][0]['code']); // First phase always setup.
313 $this->assertTrue($result['userplan']['phases'][0]['active']); // First phase "Setup" active in new workshops.
314
315 // Switch phase.
316 $workshop = new workshop($this->workshop, $this->cm, $this->course);
317 $workshop->switch_phase(workshop::PHASE_SUBMISSION);
318
319 $result = mod_workshop_external::get_user_plan($this->workshop->id);
320 $result = external_api::clean_returnvalue(mod_workshop_external::get_user_plan_returns(), $result);
321
322 $this->assertEquals(workshop::PHASE_SUBMISSION, $result['userplan']['phases'][1]['code']);
323 $this->assertTrue($result['userplan']['phases'][1]['active']); // We are now in submission phase.
324 }
325
326 /**
327 * Test mod_workshop_get_user_plan for teachers.
328 */
329 public function test_mod_workshop_get_user_plan_teacher() {
330 global $DB;
331
332 self::setUser($this->teacher);
333 $result = mod_workshop_external::get_user_plan($this->workshop->id);
334 $result = external_api::clean_returnvalue(mod_workshop_external::get_user_plan_returns(), $result);
335
336 $this->assertCount(0, $result['userplan']['examples']); // No examples given.
337 $this->assertCount(5, $result['userplan']['phases']); // Always 5 phases.
338 $this->assertEquals(workshop::PHASE_SETUP, $result['userplan']['phases'][0]['code']); // First phase always setup.
339 $this->assertTrue($result['userplan']['phases'][0]['active']); // First phase "Setup" active in new workshops.
340 $this->assertCount(4, $result['userplan']['phases'][0]['tasks']); // For new empty workshops, always 4 tasks.
341
342 foreach ($result['userplan']['phases'][0]['tasks'] as $task) {
343 if ($task['code'] == 'intro' || $task['code'] == 'instructauthors') {
344 $this->assertEquals(1, $task['completed']);
345 } else {
346 $this->assertEmpty($task['completed']);
347 }
348 }
349
350 // Do some of the tasks asked - switch phase.
351 $workshop = new workshop($this->workshop, $this->cm, $this->course);
352 $workshop->switch_phase(workshop::PHASE_SUBMISSION);
353
354 $result = mod_workshop_external::get_user_plan($this->workshop->id);
355 $result = external_api::clean_returnvalue(mod_workshop_external::get_user_plan_returns(), $result);
356 foreach ($result['userplan']['phases'][0]['tasks'] as $task) {
357 if ($task['code'] == 'intro' || $task['code'] == 'instructauthors' || $task['code'] == 'switchtonextphase') {
358 $this->assertEquals(1, $task['completed']);
359 } else {
360 $this->assertEmpty($task['completed']);
361 }
362 }
363
364 $result = mod_workshop_external::get_user_plan($this->workshop->id);
365 $result = external_api::clean_returnvalue(mod_workshop_external::get_user_plan_returns(), $result);
366
367 $this->assertEquals(workshop::PHASE_SUBMISSION, $result['userplan']['phases'][1]['code']);
368 $this->assertTrue($result['userplan']['phases'][1]['active']); // We are now in submission phase.
369 }
291645f7
JL
370
371 /**
372 * Test test_view_workshop invalid id.
373 */
374 public function test_view_workshop_invalid_id() {
375 $this->expectException('moodle_exception');
376 mod_workshop_external::view_workshop(0);
377 }
378
379 /**
380 * Test test_view_workshop user not enrolled.
381 */
382 public function test_view_workshop_user_not_enrolled() {
383 // Test not-enrolled user.
384 $usernotenrolled = self::getDataGenerator()->create_user();
385 $this->setUser($usernotenrolled);
386 $this->expectException('moodle_exception');
387 mod_workshop_external::view_workshop($this->workshop->id);
388 }
389
390 /**
391 * Test test_view_workshop user student.
392 */
393 public function test_view_workshop_user_student() {
394 // Test user with full capabilities.
395 $this->setUser($this->student);
396
397 // Trigger and capture the event.
398 $sink = $this->redirectEvents();
399
400 $result = mod_workshop_external::view_workshop($this->workshop->id);
401 $result = external_api::clean_returnvalue(mod_workshop_external::view_workshop_returns(), $result);
402 $this->assertTrue($result['status']);
403
404 $events = $sink->get_events();
405 $this->assertCount(1, $events);
406 $event = array_shift($events);
407
408 // Checking that the event contains the expected values.
409 $this->assertInstanceOf('\mod_workshop\event\course_module_viewed', $event);
410 $this->assertEquals($this->context, $event->get_context());
411 $moodleworkshop = new \moodle_url('/mod/workshop/view.php', array('id' => $this->cm->id));
412 $this->assertEquals($moodleworkshop, $event->get_url());
413 $this->assertEventContextNotUsed($event);
414 $this->assertNotEmpty($event->get_name());
415 }
416
417 /**
418 * Test test_view_workshop user missing capabilities.
419 */
420 public function test_view_workshop_user_missing_capabilities() {
421 // Test user with no capabilities.
422 // We need a explicit prohibit since this capability is only defined in authenticated user and guest roles.
423 assign_capability('mod/workshop:view', CAP_PROHIBIT, $this->studentrole->id, $this->context->id);
424 // Empty all the caches that may be affected by this change.
425 accesslib_clear_all_caches_for_unit_testing();
426 course_modinfo::clear_instance_cache();
427
428 $this->setUser($this->student);
429 $this->expectException('moodle_exception');
430 mod_workshop_external::view_workshop($this->workshop->id);
431 }
c2cf2450
JL
432
433 /**
434 * Test test_add_submission.
435 */
436 public function test_add_submission() {
437 $fs = get_file_storage();
438
439 // Test user with full capabilities.
440 $this->setUser($this->student);
441
442 $title = 'Submission title';
443 $content = 'Submission contents';
444
445 // Create a file in a draft area for inline attachments.
446 $draftidinlineattach = file_get_unused_draft_itemid();
447 $usercontext = context_user::instance($this->student->id);
448 $filenameimg = 'shouldbeanimage.txt';
449 $filerecordinline = array(
450 'contextid' => $usercontext->id,
451 'component' => 'user',
452 'filearea' => 'draft',
453 'itemid' => $draftidinlineattach,
454 'filepath' => '/',
455 'filename' => $filenameimg,
456 );
457 $fs->create_file_from_string($filerecordinline, 'image contents (not really)');
458
459 // Create a file in a draft area for regular attachments.
460 $draftidattach = file_get_unused_draft_itemid();
461 $filerecordattach = $filerecordinline;
462 $attachfilename = 'attachment.txt';
463 $filerecordattach['filename'] = $attachfilename;
464 $filerecordattach['itemid'] = $draftidattach;
465 $fs->create_file_from_string($filerecordattach, 'simple text attachment');
466
467 // Switch to submission phase.
468 $workshop = new workshop($this->workshop, $this->cm, $this->course);
469 $workshop->switch_phase(workshop::PHASE_SUBMISSION);
470
471 $result = mod_workshop_external::add_submission($this->workshop->id, $title, $content, FORMAT_MOODLE, $draftidinlineattach,
472 $draftidattach);
473 $result = external_api::clean_returnvalue(mod_workshop_external::add_submission_returns(), $result);
474 $this->assertEmpty($result['warnings']);
475
476 // Check submission created.
477 $submission = $workshop->get_submission_by_author($this->student->id);
4834e127 478 $this->assertTrue($result['status']);
c2cf2450
JL
479 $this->assertEquals($result['submissionid'], $submission->id);
480 $this->assertEquals($title, $submission->title);
481 $this->assertEquals($content, $submission->content);
482
483 // Check files.
484 $contentfiles = $fs->get_area_files($this->context->id, 'mod_workshop', 'submission_content', $submission->id);
485 $this->assertCount(2, $contentfiles);
486 foreach ($contentfiles as $file) {
487 if ($file->is_directory()) {
488 continue;
489 } else {
490 $this->assertEquals($filenameimg, $file->get_filename());
491 }
492 }
493 $contentfiles = $fs->get_area_files($this->context->id, 'mod_workshop', 'submission_attachment', $submission->id);
494 $this->assertCount(2, $contentfiles);
495 foreach ($contentfiles as $file) {
496 if ($file->is_directory()) {
497 continue;
498 } else {
499 $this->assertEquals($attachfilename, $file->get_filename());
500 }
501 }
502 }
503
504 /**
505 * Test test_add_submission invalid phase.
506 */
507 public function test_add_submission_invalid_phase() {
508 $this->setUser($this->student);
509
510 $this->expectException('moodle_exception');
511 mod_workshop_external::add_submission($this->workshop->id, 'Test');
512 }
513
514 /**
515 * Test test_add_submission empty title.
516 */
517 public function test_add_submission_empty_title() {
518 $this->setUser($this->student);
519
520 // Switch to submission phase.
521 $workshop = new workshop($this->workshop, $this->cm, $this->course);
522 $workshop->switch_phase(workshop::PHASE_SUBMISSION);
523
524 $this->expectException('moodle_exception');
525 mod_workshop_external::add_submission($this->workshop->id, '');
526 }
527
528 /**
529 * Test test_add_submission already added.
530 */
531 public function test_add_submission_already_added() {
532 $this->setUser($this->student);
533
534 // Switch to submission phase.
535 $workshop = new workshop($this->workshop, $this->cm, $this->course);
536 $workshop->switch_phase(workshop::PHASE_SUBMISSION);
537
538 // Create the submission.
539 $result = mod_workshop_external::add_submission($this->workshop->id, 'My submission');
540 $result = external_api::clean_returnvalue(mod_workshop_external::add_submission_returns(), $result);
541
542 // Try to create it again.
543 $result = mod_workshop_external::add_submission($this->workshop->id, 'My submission');
544 $result = external_api::clean_returnvalue(mod_workshop_external::add_submission_returns(), $result);
4834e127
JL
545 $this->assertFalse($result['status']);
546 $this->assertArrayNotHasKey('submissionid', $result);
c2cf2450
JL
547 $this->assertCount(2, $result['warnings']);
548 $this->assertEquals('fielderror', $result['warnings'][0]['warningcode']);
549 $this->assertEquals('content_editor', $result['warnings'][0]['item']);
550 $this->assertEquals('fielderror', $result['warnings'][1]['warningcode']);
551 $this->assertEquals('attachment_filemanager', $result['warnings'][1]['item']);
552 }
c1698a37
JL
553
554 /**
555 * Helper method to create a submission for testing for the given user.
556 *
557 * @param int $user the submission will be created by this student.
558 * @return int the submission id
559 */
560 protected function create_test_submission($user) {
561 // Test user with full capabilities.
562 $this->setUser($user);
563
564 $title = 'Submission title';
565 $content = 'Submission contents';
566
567 // Create a file in a draft area for inline attachments.
568 $fs = get_file_storage();
569 $draftidinlineattach = file_get_unused_draft_itemid();
570 $usercontext = context_user::instance($this->student->id);
571 $filenameimg = 'shouldbeanimage.txt';
572 $filerecordinline = array(
573 'contextid' => $usercontext->id,
574 'component' => 'user',
575 'filearea' => 'draft',
576 'itemid' => $draftidinlineattach,
577 'filepath' => '/',
578 'filename' => $filenameimg,
579 );
580 $fs->create_file_from_string($filerecordinline, 'image contents (not really)');
581
582 // Create a file in a draft area for regular attachments.
583 $draftidattach = file_get_unused_draft_itemid();
584 $filerecordattach = $filerecordinline;
585 $attachfilename = 'attachment.txt';
586 $filerecordattach['filename'] = $attachfilename;
587 $filerecordattach['itemid'] = $draftidattach;
588 $fs->create_file_from_string($filerecordattach, 'simple text attachment');
589
590 // Switch to submission phase.
591 $workshop = new workshop($this->workshop, $this->cm, $this->course);
592 $workshop->switch_phase(workshop::PHASE_SUBMISSION);
593
594 $result = mod_workshop_external::add_submission($this->workshop->id, $title, $content, FORMAT_MOODLE, $draftidinlineattach,
595 $draftidattach);
596 return $result['submissionid'];
597 }
598
599 /**
600 * Test test_update_submission.
601 */
602 public function test_update_submission() {
603
604 // Create the submission that will be updated.
605 $submissionid = $this->create_test_submission($this->student);
606
607 // Test user with full capabilities.
608 $this->setUser($this->student);
609
610 $title = 'Submission new title';
611 $content = 'Submission new contents';
612
613 // Create a different file in a draft area for inline attachments.
614 $fs = get_file_storage();
615 $draftidinlineattach = file_get_unused_draft_itemid();
616 $usercontext = context_user::instance($this->student->id);
617 $filenameimg = 'shouldbeanimage_new.txt';
618 $filerecordinline = array(
619 'contextid' => $usercontext->id,
620 'component' => 'user',
621 'filearea' => 'draft',
622 'itemid' => $draftidinlineattach,
623 'filepath' => '/',
624 'filename' => $filenameimg,
625 );
626 $fs->create_file_from_string($filerecordinline, 'image contents (not really)');
627
628 // Create a different file in a draft area for regular attachments.
629 $draftidattach = file_get_unused_draft_itemid();
630 $filerecordattach = $filerecordinline;
631 $attachfilename = 'attachment_new.txt';
632 $filerecordattach['filename'] = $attachfilename;
633 $filerecordattach['itemid'] = $draftidattach;
634 $fs->create_file_from_string($filerecordattach, 'simple text attachment');
635
636 $result = mod_workshop_external::update_submission($submissionid, $title, $content, FORMAT_MOODLE, $draftidinlineattach,
637 $draftidattach);
638 $result = external_api::clean_returnvalue(mod_workshop_external::update_submission_returns(), $result);
639 $this->assertEmpty($result['warnings']);
640
641 // Check submission updated.
642 $workshop = new workshop($this->workshop, $this->cm, $this->course);
643 $submission = $workshop->get_submission_by_id($submissionid);
644 $this->assertTrue($result['status']);
645 $this->assertEquals($title, $submission->title);
646 $this->assertEquals($content, $submission->content);
647
648 // Check files.
649 $contentfiles = $fs->get_area_files($this->context->id, 'mod_workshop', 'submission_content', $submission->id);
650 $this->assertCount(2, $contentfiles);
651 foreach ($contentfiles as $file) {
652 if ($file->is_directory()) {
653 continue;
654 } else {
655 $this->assertEquals($filenameimg, $file->get_filename());
656 }
657 }
658 $contentfiles = $fs->get_area_files($this->context->id, 'mod_workshop', 'submission_attachment', $submission->id);
659 $this->assertCount(2, $contentfiles);
660 foreach ($contentfiles as $file) {
661 if ($file->is_directory()) {
662 continue;
663 } else {
664 $this->assertEquals($attachfilename, $file->get_filename());
665 }
666 }
667 }
668
669 /**
670 * Test test_update_submission belonging to other user.
671 */
672 public function test_update_submission_of_other_user() {
673 // Create the submission that will be updated.
674 $submissionid = $this->create_test_submission($this->student);
675
676 $this->setUser($this->teacher);
677
678 $this->expectException('moodle_exception');
679 mod_workshop_external::update_submission($submissionid, 'Test');
680 }
681
682 /**
683 * Test test_update_submission invalid phase.
684 */
685 public function test_update_submission_invalid_phase() {
686 // Create the submission that will be updated.
687 $submissionid = $this->create_test_submission($this->student);
688
689 $this->setUser($this->student);
690
691 // Switch to assessment phase.
692 $workshop = new workshop($this->workshop, $this->cm, $this->course);
693 $workshop->switch_phase(workshop::PHASE_ASSESSMENT);
694
695 $this->expectException('moodle_exception');
696 mod_workshop_external::update_submission($submissionid, 'Test');
697 }
698
699 /**
700 * Test test_update_submission empty title.
701 */
702 public function test_update_submission_empty_title() {
703 // Create the submission that will be updated.
704 $submissionid = $this->create_test_submission($this->student);
705
706 $this->setUser($this->student);
707
708 $this->expectException('moodle_exception');
709 mod_workshop_external::update_submission($submissionid, '');
710 }
bde5631d
JL
711
712 /**
713 * Test test_delete_submission.
714 */
715 public function test_delete_submission() {
716
717 // Create the submission that will be deleted.
718 $submissionid = $this->create_test_submission($this->student);
719
720 $this->setUser($this->student);
721
722 // Trigger and capture the event.
723 $sink = $this->redirectEvents();
724
725 $result = mod_workshop_external::delete_submission($submissionid);
726 $result = external_api::clean_returnvalue(mod_workshop_external::delete_submission_returns(), $result);
727 $this->assertEmpty($result['warnings']);
728 $this->assertTrue($result['status']);
729 $workshop = new workshop($this->workshop, $this->cm, $this->course);
730 $submission = $workshop->get_submission_by_author($this->student->id);
731 $this->assertFalse($submission);
732
733 $events = $sink->get_events();
734 $this->assertCount(1, $events);
735 $event = array_shift($events);
736
737 // Checking event.
738 $this->assertInstanceOf('\mod_workshop\event\submission_deleted', $event);
739 $this->assertEquals($this->context, $event->get_context());
740 }
741
742 /**
743 * Test test_delete_submission_with_assessments.
744 */
745 public function test_delete_submission_with_assessments() {
746
747 // Create the submission that will be deleted.
748 $submissionid = $this->create_test_submission($this->student);
749
750 $workshopgenerator = $this->getDataGenerator()->get_plugin_generator('mod_workshop');
751 $workshopgenerator->create_assessment($submissionid, $this->teacher->id, array(
752 'weight' => 3,
753 'grade' => 95.00000,
754 ));
755
756 $this->setUser($this->student);
757 $this->expectException('moodle_exception');
758 mod_workshop_external::delete_submission($submissionid);
759 }
760
761 /**
762 * Test test_delete_submission_invalid_phase.
763 */
764 public function test_delete_submission_invalid_phase() {
765
766 // Create the submission that will be deleted.
767 $submissionid = $this->create_test_submission($this->student);
768
769 // Switch to assessment phase.
770 $workshop = new workshop($this->workshop, $this->cm, $this->course);
771 $workshop->switch_phase(workshop::PHASE_ASSESSMENT);
772
773 $this->setUser($this->student);
774 $this->expectException('moodle_exception');
775 mod_workshop_external::delete_submission($submissionid);
776 }
777
778 /**
779 * Test test_delete_submission_as_teacher.
780 */
781 public function test_delete_submission_as_teacher() {
782
783 // Create the submission that will be deleted.
784 $submissionid = $this->create_test_submission($this->student);
785
786 $this->setUser($this->teacher);
787 $result = mod_workshop_external::delete_submission($submissionid);
788 $result = external_api::clean_returnvalue(mod_workshop_external::delete_submission_returns(), $result);
789 $this->assertEmpty($result['warnings']);
790 $this->assertTrue($result['status']);
791 }
792
793 /**
794 * Test test_delete_submission_other_user.
795 */
796 public function test_delete_submission_other_user() {
797
798 $anotheruser = self::getDataGenerator()->create_user();
799 $this->getDataGenerator()->enrol_user($anotheruser->id, $this->course->id, $this->studentrole->id, 'manual');
800 // Create the submission that will be deleted.
801 $submissionid = $this->create_test_submission($this->student);
802
803 $this->setUser($anotheruser);
804 $this->expectException('moodle_exception');
805 mod_workshop_external::delete_submission($submissionid);
806 }
3f08cfc5
JL
807
808 /**
809 * Test test_get_submissions_student.
810 */
811 public function test_get_submissions_student() {
812
813 // Create a couple of submissions with files.
814 $firstsubmissionid = $this->create_test_submission($this->student); // Create submission with files.
815 $secondsubmissionid = $this->create_test_submission($this->anotherstudentg1->id);
816
817 $this->setUser($this->student);
818 $result = mod_workshop_external::get_submissions($this->workshop->id);
819 $result = external_api::clean_returnvalue(mod_workshop_external::get_submissions_returns(), $result);
820 // We should get just our submission.
821 $this->assertCount(1, $result['submissions']);
822 $this->assertEquals(1, $result['totalcount']);
823 $this->assertEquals($firstsubmissionid, $result['submissions'][0]['id']);
824 $this->assertCount(1, $result['submissions'][0]['contentfiles']); // Check we retrieve submission text files.
825 $this->assertCount(1, $result['submissions'][0]['attachmentfiles']); // Check we retrieve attachment files.
826 // We shoul not see the grade or feedback information.
827 $properties = submission_exporter::properties_definition();
828 foreach ($properties as $attribute => $settings) {
829 if (!empty($settings['optional'])) {
830 if (isset($result['submissions'][0][$attribute])) {
831 echo "error $attribute";
832 }
833 $this->assertFalse(isset($result['submissions'][0][$attribute]));
834 }
835 }
836 }
837
838 /**
839 * Test test_get_submissions_published_student.
840 */
841 public function test_get_submissions_published_student() {
842 global $DB;
843
844 $DB->set_field('workshop', 'phase', workshop::PHASE_CLOSED, array('id' => $this->workshop->id));
845 // Create a couple of submissions with files.
846 $workshopgenerator = $this->getDataGenerator()->get_plugin_generator('mod_workshop');
847 $submission = array('published' => 1);
848 $submissionid = $workshopgenerator->create_submission($this->workshop->id, $this->anotherstudentg1->id, $submission);
849
850 $this->setUser($this->student);
851 $result = mod_workshop_external::get_submissions($this->workshop->id);
852 $result = external_api::clean_returnvalue(mod_workshop_external::get_submissions_returns(), $result);
853 // We should get just our submission.
854 $this->assertCount(1, $result['submissions']);
855 $this->assertEquals(1, $result['totalcount']);
856 $this->assertEquals($submissionid, $result['submissions'][0]['id']);
857
858 // Check with group restrictions.
859 $this->setUser($this->anotherstudentg2);
860 $result = mod_workshop_external::get_submissions($this->workshop->id);
861 $result = external_api::clean_returnvalue(mod_workshop_external::get_submissions_returns(), $result);
862 $this->assertCount(0, $result['submissions']); // I can't see other users in separated groups.
863 $this->assertEquals(0, $result['totalcount']);
864 }
865
866 /**
867 * Test test_get_submissions_from_student_with_feedback_from_teacher.
868 */
869 public function test_get_submissions_from_student_with_feedback_from_teacher() {
870 global $DB;
871
872 // Create a couple of submissions with files.
873 $workshopgenerator = $this->getDataGenerator()->get_plugin_generator('mod_workshop');
874 $submissionid = $workshopgenerator->create_submission($this->workshop->id, $this->student->id);
875 // Create teacher feedback for submission.
876 $record = new stdclass();
877 $record->id = $submissionid;
878 $record->gradeover = 9;
879 $record->gradeoverby = $this->teacher->id;
880 $record->feedbackauthor = 'Hey';
881 $record->feedbackauthorformat = FORMAT_MOODLE;
882 $record->published = 1;
883 $DB->update_record('workshop_submissions', $record);
884
885 // Remove teacher caps.
886 assign_capability('mod/workshop:viewallsubmissions', CAP_PROHIBIT, $this->teacher->id, $this->context->id);
887 // Empty all the caches that may be affected by this change.
888 accesslib_clear_all_caches_for_unit_testing();
889 course_modinfo::clear_instance_cache();
890
891 $this->setUser($this->teacher);
892 $result = mod_workshop_external::get_submissions($this->workshop->id, $this->student->id);
893 $result = external_api::clean_returnvalue(mod_workshop_external::get_submissions_returns(), $result);
894 // We should get just our submission.
895 $this->assertEquals(1, $result['totalcount']);
896 $this->assertEquals($submissionid, $result['submissions'][0]['id']);
897 }
898
899 /**
900 * Test test_get_submissions_from_students_as_teacher.
901 */
902 public function test_get_submissions_from_students_as_teacher() {
903 global $DB;
904
905 // Create a couple of submissions with files.
906 $workshopgenerator = $this->getDataGenerator()->get_plugin_generator('mod_workshop');
907 $submissionid1 = $workshopgenerator->create_submission($this->workshop->id, $this->student->id);
908 $submissionid2 = $workshopgenerator->create_submission($this->workshop->id, $this->anotherstudentg1->id);
909 $submissionid3 = $workshopgenerator->create_submission($this->workshop->id, $this->anotherstudentg2->id);
910
911 $this->setUser($this->teacher);
912 $result = mod_workshop_external::get_submissions($this->workshop->id); // Get all.
913 $result = external_api::clean_returnvalue(mod_workshop_external::get_submissions_returns(), $result);
914 $this->assertEquals(3, $result['totalcount']);
915 $this->assertCount(3, $result['submissions']);
916
917 $result = mod_workshop_external::get_submissions($this->workshop->id, 0, 0, 0, 2); // Check pagination.
918 $result = external_api::clean_returnvalue(mod_workshop_external::get_submissions_returns(), $result);
919 $this->assertEquals(3, $result['totalcount']);
920 $this->assertCount(2, $result['submissions']);
921
922 $result = mod_workshop_external::get_submissions($this->workshop->id, 0, $this->group2->id); // Get group 2.
923 $result = external_api::clean_returnvalue(mod_workshop_external::get_submissions_returns(), $result);
924 $this->assertEquals(1, $result['totalcount']);
925 $this->assertCount(1, $result['submissions']);
926 $this->assertEquals($submissionid3, $result['submissions'][0]['id']);
927
928 $result = mod_workshop_external::get_submissions($this->workshop->id, $this->anotherstudentg1->id); // Get one.
929 $result = external_api::clean_returnvalue(mod_workshop_external::get_submissions_returns(), $result);
930 $this->assertEquals(1, $result['totalcount']);
931 $this->assertEquals($submissionid2, $result['submissions'][0]['id']);
932 }
6e2f4866
JL
933
934 /**
935 * Test test_get_submission_student.
936 */
937 public function test_get_submission_student() {
938
939 // Create a couple of submissions with files.
940 $firstsubmissionid = $this->create_test_submission($this->student); // Create submission with files.
941
942 $this->setUser($this->student);
943 $result = mod_workshop_external::get_submission($firstsubmissionid);
944 $result = external_api::clean_returnvalue(mod_workshop_external::get_submission_returns(), $result);
945 $this->assertEquals($firstsubmissionid, $result['submission']['id']);
946 $this->assertCount(1, $result['submission']['contentfiles']); // Check we retrieve submission text files.
947 $this->assertCount(1, $result['submission']['attachmentfiles']); // Check we retrieve attachment files.
948 }
949
950 /**
951 * Test test_get_submission_i_reviewed.
952 */
953 public function test_get_submission_i_reviewed() {
954
955 // Create a couple of submissions with files.
956 $firstsubmissionid = $this->create_test_submission($this->student); // Create submission with files.
957 $workshopgenerator = $this->getDataGenerator()->get_plugin_generator('mod_workshop');
958 $workshopgenerator->create_assessment($firstsubmissionid, $this->anotherstudentg1->id, array(
959 'weight' => 3,
960 'grade' => 95,
961 ));
962 // Now try to get the submission I just reviewed.
963 $this->setUser($this->anotherstudentg1);
964 $result = mod_workshop_external::get_submission($firstsubmissionid);
965 $result = external_api::clean_returnvalue(mod_workshop_external::get_submission_returns(), $result);
966 $this->assertEquals($firstsubmissionid, $result['submission']['id']);
967 $this->assertCount(1, $result['submission']['contentfiles']); // Check we retrieve submission text files.
968 $this->assertCount(1, $result['submission']['attachmentfiles']); // Check we retrieve attachment files.
969 }
970
971 /**
972 * Test test_get_submission_other_student.
973 */
974 public function test_get_submission_other_student() {
975
976 // Create a couple of submissions with files.
977 $firstsubmissionid = $this->create_test_submission($this->student); // Create submission with files.
978 // Expect failure.
979 $this->setUser($this->anotherstudentg1);
980 $this->expectException('moodle_exception');
981 $result = mod_workshop_external::get_submission($firstsubmissionid);
982 }
983
984 /**
985 * Test test_get_submission_published_student.
986 */
987 public function test_get_submission_published_student() {
988 global $DB;
989
990 $DB->set_field('workshop', 'phase', workshop::PHASE_CLOSED, array('id' => $this->workshop->id));
991 // Create a couple of submissions with files.
992 $workshopgenerator = $this->getDataGenerator()->get_plugin_generator('mod_workshop');
993 $submission = array('published' => 1);
994 $submissionid = $workshopgenerator->create_submission($this->workshop->id, $this->anotherstudentg1->id, $submission);
995
996 $this->setUser($this->student);
997 $result = mod_workshop_external::get_submission($submissionid);
998 $result = external_api::clean_returnvalue(mod_workshop_external::get_submission_returns(), $result);
999 $this->assertEquals($submissionid, $result['submission']['id']);
1000 // Check that the student don't see the other student grade/feedback data even if is published.
1001 // We shoul not see the grade or feedback information.
1002 $properties = submission_exporter::properties_definition();
1003 foreach ($properties as $attribute => $settings) {
1004 if (!empty($settings['optional'])) {
1005 if (isset($result['submission'][$attribute])) {
1006 echo "error $attribute";
1007 }
1008 $this->assertFalse(isset($result['submission'][$attribute]));
1009 }
1010 }
1011
1012 // Check with group restrictions.
1013 $this->setUser($this->anotherstudentg2);
1014 $this->expectException('moodle_exception');
1015 mod_workshop_external::get_submission($submissionid);
1016 }
1017
1018 /**
1019 * Test test_get_submission_from_student_with_feedback_from_teacher.
1020 */
1021 public function test_get_submission_from_student_with_feedback_from_teacher() {
1022 global $DB;
1023
1024 // Create a couple of submissions with files.
1025 $workshopgenerator = $this->getDataGenerator()->get_plugin_generator('mod_workshop');
1026 $submissionid = $workshopgenerator->create_submission($this->workshop->id, $this->student->id);
1027 // Create teacher feedback for submission.
1028 $record = new stdclass();
1029 $record->id = $submissionid;
1030 $record->gradeover = 9;
1031 $record->gradeoverby = $this->teacher->id;
1032 $record->feedbackauthor = 'Hey';
1033 $record->feedbackauthorformat = FORMAT_MOODLE;
1034 $record->published = 1;
1035 $DB->update_record('workshop_submissions', $record);
1036
1037 // Remove teacher caps.
1038 assign_capability('mod/workshop:viewallsubmissions', CAP_PROHIBIT, $this->teacher->id, $this->context->id);
1039 // Empty all the caches that may be affected by this change.
1040 accesslib_clear_all_caches_for_unit_testing();
1041 course_modinfo::clear_instance_cache();
1042
1043 $this->setUser($this->teacher);
1044 $result = mod_workshop_external::get_submission($submissionid);
1045 $result = external_api::clean_returnvalue(mod_workshop_external::get_submission_returns(), $result);
1046 $this->assertEquals($submissionid, $result['submission']['id']);
1047 }
1048
1049 /**
1050 * Test test_get_submission_from_students_as_teacher.
1051 */
1052 public function test_get_submission_from_students_as_teacher() {
1053 // Create a couple of submissions with files.
1054 $workshopgenerator = $this->getDataGenerator()->get_plugin_generator('mod_workshop');
1055 $submissionid1 = $workshopgenerator->create_submission($this->workshop->id, $this->student->id);
1056 $submissionid2 = $workshopgenerator->create_submission($this->workshop->id, $this->anotherstudentg1->id);
1057 $submissionid3 = $workshopgenerator->create_submission($this->workshop->id, $this->anotherstudentg2->id);
1058
1059 $this->setUser($this->teacher);
1060 $result = mod_workshop_external::get_submission($submissionid1); // Get all.
1061 $result = external_api::clean_returnvalue(mod_workshop_external::get_submission_returns(), $result);
1062 $this->assertEquals($submissionid1, $result['submission']['id']);
1063
1064 $result = mod_workshop_external::get_submission($submissionid3); // Get group 2.
1065 $result = external_api::clean_returnvalue(mod_workshop_external::get_submission_returns(), $result);
1066 $this->assertEquals($submissionid3, $result['submission']['id']);
1067 }
30b54b82
JL
1068
1069
1070 /**
1071 * Test get_submission_assessments_student.
1072 */
1073 public function test_get_submission_assessments_student() {
1074 global $DB;
1075
1076 // Create the submission that will be deleted.
1077 $submissionid = $this->create_test_submission($this->student);
1078
1079 $workshopgenerator = $this->getDataGenerator()->get_plugin_generator('mod_workshop');
1080 $workshopgenerator->create_assessment($submissionid, $this->anotherstudentg1->id, array(
1081 'weight' => 3,
1082 'grade' => 95,
1083 ));
1084 $workshopgenerator->create_assessment($submissionid, $this->student->id, array(
1085 'weight' => 2,
1086 'grade' => 90,
1087 ));
1088
1089 $DB->set_field('workshop', 'phase', workshop::PHASE_CLOSED, array('id' => $this->workshop->id));
1090 $this->setUser($this->student);
1091 $result = mod_workshop_external::get_submission_assessments($submissionid);
1092 $result = external_api::clean_returnvalue(mod_workshop_external::get_submission_assessments_returns(), $result);
1093 $this->assertCount(2, $result['assessments']); // I received my two assessments.
1094 foreach ($result['assessments'] as $assessment) {
1095 if ($assessment['grade'] == 90) {
1096 // My own assessment, I can see me.
1097 $this->assertEquals($this->student->id, $assessment['reviewerid']);
1098 } else {
1099 // Student's can't see who did the review.
1100 $this->assertEquals(0, $assessment['reviewerid']);
1101 }
1102 }
1103 }
1104
1105 /**
1106 * Test get_submission_assessments_invalid_phase.
1107 */
1108 public function test_get_submission_assessments_invalid_phase() {
1109 global $DB;
1110
1111 // Create the submission that will be deleted.
1112 $submissionid = $this->create_test_submission($this->student);
1113
1114 $workshopgenerator = $this->getDataGenerator()->get_plugin_generator('mod_workshop');
1115 $workshopgenerator->create_assessment($submissionid, $this->anotherstudentg1->id, array(
1116 'weight' => 3,
1117 'grade' => 95,
1118 ));
1119
1120 $this->expectException('moodle_exception');
1121 mod_workshop_external::get_submission_assessments($submissionid);
1122 }
1123
1124 /**
1125 * Test get_submission_assessments_teacher.
1126 */
1127 public function test_get_submission_assessments_teacher() {
1128
1129 // Create the submission that will be deleted.
1130 $submissionid = $this->create_test_submission($this->student);
1131
1132 $workshopgenerator = $this->getDataGenerator()->get_plugin_generator('mod_workshop');
1133 $assessmentid = $workshopgenerator->create_assessment($submissionid, $this->anotherstudentg1->id, array(
1134 'weight' => 1,
1135 'grade' => 50,
1136 ));
1137
1138 $this->setUser($this->teacher);
1139 $result = mod_workshop_external::get_submission_assessments($submissionid);
1140 $result = external_api::clean_returnvalue(mod_workshop_external::get_submission_assessments_returns(), $result);
1141 $this->assertCount(1, $result['assessments']);
1142 $this->assertEquals(50, $result['assessments'][0]['grade']);
1143 $this->assertEquals($assessmentid, $result['assessments'][0]['id']);
1144 }
9f1ab2db 1145}