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;
35use mod_workshop\external\submission_exporter;
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.
74        $course = new stdClass();
75        $course->groupmode = SEPARATEGROUPS;
76        $course->groupmodeforce = true;
77        $this->course = $this->getDataGenerator()->create_course($course);
78        $this->workshop = $this->getDataGenerator()->create_module('workshop',
79            array(
80                'course' => $this->course->id,
81                'overallfeedbackfiles' => 1,
82            )
83        );
84        $this->context = context_module::instance($this->workshop->cmid);
85        $this->cm = get_coursemodule_from_instance('workshop', $this->workshop->id);
86
87        // Add grading strategy data (accumulative is the default).
88        $workshop = new workshop($this->workshop, $this->cm, $this->course);
89        $strategy = $workshop->grading_strategy_instance();
90        $data = array();
91        for ($i = 0; $i < 4; $i++) {
92            $data['dimensionid__idx_'.$i] = 0;
93            $data['description__idx_'.$i.'_editor'] = array('text' => "Content $i", 'format' => FORMAT_MOODLE);
94            $data['grade__idx_'.$i] = 25;
95            $data['weight__idx_'.$i] = 25;
96        }
97        $data['workshopid'] = $workshop->id;
98        $data['norepeats'] = 4;
99        $strategy->save_edit_strategy_form((object) $data);
100
101        // Create users.
102        $this->student = self::getDataGenerator()->create_user();
103        $this->anotherstudentg1 = self::getDataGenerator()->create_user();
104        $this->anotherstudentg2 = self::getDataGenerator()->create_user();
105        $this->teacher = self::getDataGenerator()->create_user();
106
107        // Users enrolments.
108        $this->studentrole = $DB->get_record('role', array('shortname' => 'student'));
109        $this->teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'));
110        $this->getDataGenerator()->enrol_user($this->student->id, $this->course->id, $this->studentrole->id, 'manual');
111        $this->getDataGenerator()->enrol_user($this->anotherstudentg1->id, $this->course->id, $this->studentrole->id, 'manual');
112        $this->getDataGenerator()->enrol_user($this->anotherstudentg2->id, $this->course->id, $this->studentrole->id, 'manual');
113        $this->getDataGenerator()->enrol_user($this->teacher->id, $this->course->id, $this->teacherrole->id, 'manual');
114
115        $this->group1 = $this->getDataGenerator()->create_group(array('courseid' => $this->course->id));
116        $this->group2 = $this->getDataGenerator()->create_group(array('courseid' => $this->course->id));
117        groups_add_member($this->group1, $this->student);
118        groups_add_member($this->group1, $this->anotherstudentg1);
119        groups_add_member($this->group2, $this->anotherstudentg2);
120    }
121
122    /**
123     * Test test_mod_workshop_get_workshops_by_courses
124     */
125    public function test_mod_workshop_get_workshops_by_courses() {
126
127        // Create additional course.
128        $course2 = self::getDataGenerator()->create_course();
129
130        // Second workshop.
131        $record = new stdClass();
132        $record->course = $course2->id;
133        $workshop2 = self::getDataGenerator()->create_module('workshop', $record);
134
135        // Execute real Moodle enrolment as we'll call unenrol() method on the instance later.
136        $enrol = enrol_get_plugin('manual');
137        $enrolinstances = enrol_get_instances($course2->id, true);
138        foreach ($enrolinstances as $courseenrolinstance) {
139            if ($courseenrolinstance->enrol == "manual") {
140                $instance2 = $courseenrolinstance;
141                break;
142            }
143        }
144        $enrol->enrol_user($instance2, $this->student->id, $this->studentrole->id);
145
146        self::setUser($this->student);
147
148        $returndescription = mod_workshop_external::get_workshops_by_courses_returns();
149
150        // Create what we expect to be returned when querying the two courses.
151        $properties = workshop_summary_exporter::read_properties_definition();
152        $expectedfields = array_keys($properties);
153
154        // Add expected coursemodule and data.
155        $workshop1 = $this->workshop;
156        $workshop1->coursemodule = $workshop1->cmid;
157        $workshop1->introformat = 1;
158        $workshop1->introfiles = [];
159        $workshop1->instructauthorsfiles = [];
160        $workshop1->instructauthorsformat = 1;
161        $workshop1->instructreviewersfiles = [];
162        $workshop1->instructreviewersformat = 1;
163        $workshop1->conclusionfiles = [];
164        $workshop1->conclusionformat = 1;
165        $workshop1->submissiontypetext = 1;
166        $workshop1->submissiontypefile = 1;
167
168        $workshop2->coursemodule = $workshop2->cmid;
169        $workshop2->introformat = 1;
170        $workshop2->introfiles = [];
171        $workshop2->instructauthorsfiles = [];
172        $workshop2->instructauthorsformat = 1;
173        $workshop2->instructreviewersfiles = [];
174        $workshop2->instructreviewersformat = 1;
175        $workshop2->conclusionfiles = [];
176        $workshop2->conclusionformat = 1;
177        $workshop2->submissiontypetext = 1;
178        $workshop2->submissiontypefile = 1;
179
180        foreach ($expectedfields as $field) {
181            if (!empty($properties[$field]) && $properties[$field]['type'] == PARAM_BOOL) {
182                $workshop1->{$field} = (bool) $workshop1->{$field};
183                $workshop2->{$field} = (bool) $workshop2->{$field};
184            }
185            $expected1[$field] = $workshop1->{$field};
186            $expected2[$field] = $workshop2->{$field};
187        }
188
189        $expectedworkshops = array($expected2, $expected1);
190
191        // Call the external function passing course ids.
192        $result = mod_workshop_external::get_workshops_by_courses(array($course2->id, $this->course->id));
193        $result = external_api::clean_returnvalue($returndescription, $result);
194
195        $this->assertEquals($expectedworkshops, $result['workshops']);
196        $this->assertCount(0, $result['warnings']);
197
198        // Call the external function without passing course id.
199        $result = mod_workshop_external::get_workshops_by_courses();
200        $result = external_api::clean_returnvalue($returndescription, $result);
201        $this->assertEquals($expectedworkshops, $result['workshops']);
202        $this->assertCount(0, $result['warnings']);
203
204        // Unenrol user from second course and alter expected workshops.
205        $enrol->unenrol_user($instance2, $this->student->id);
206        array_shift($expectedworkshops);
207
208        // Call the external function without passing course id.
209        $result = mod_workshop_external::get_workshops_by_courses();
210        $result = external_api::clean_returnvalue($returndescription, $result);
211        $this->assertEquals($expectedworkshops, $result['workshops']);
212
213        // Call for the second course we unenrolled the user from, expected warning.
214        $result = mod_workshop_external::get_workshops_by_courses(array($course2->id));
215        $this->assertCount(1, $result['warnings']);
216        $this->assertEquals('1', $result['warnings'][0]['warningcode']);
217        $this->assertEquals($course2->id, $result['warnings'][0]['itemid']);
218    }
219
220    /**
221     * Test mod_workshop_get_workshop_access_information for students.
222     */
223    public function test_mod_workshop_get_workshop_access_information_student() {
224
225        self::setUser($this->student);
226        $result = mod_workshop_external::get_workshop_access_information($this->workshop->id);
227        $result = external_api::clean_returnvalue(mod_workshop_external::get_workshop_access_information_returns(), $result);
228        // Check default values for capabilities.
229        $enabledcaps = array('canpeerassess', 'cansubmit', 'canview', 'canviewauthornames', 'canviewauthorpublished',
230            'canviewpublishedsubmissions', 'canexportsubmissions');
231
232        foreach ($result as $capname => $capvalue) {
233            if (strpos($capname, 'can') !== 0) {
234                continue;
235            }
236            if (in_array($capname, $enabledcaps)) {
237                $this->assertTrue($capvalue);
238            } else {
239                $this->assertFalse($capvalue);
240            }
241        }
242        // Now, unassign some capabilities.
243        unassign_capability('mod/workshop:peerassess', $this->studentrole->id);
244        unassign_capability('mod/workshop:submit', $this->studentrole->id);
245        unset($enabledcaps[0]);
246        unset($enabledcaps[1]);
247        accesslib_clear_all_caches_for_unit_testing();
248
249        $result = mod_workshop_external::get_workshop_access_information($this->workshop->id);
250        $result = external_api::clean_returnvalue(mod_workshop_external::get_workshop_access_information_returns(), $result);
251        foreach ($result as $capname => $capvalue) {
252            if (strpos($capname, 'can') !== 0) {
253                continue;
254            }
255            if (in_array($capname, $enabledcaps)) {
256                $this->assertTrue($capvalue);
257            } else {
258                $this->assertFalse($capvalue);
259            }
260        }
261
262        // Now, specific functionalities.
263        $this->assertFalse($result['creatingsubmissionallowed']);
264        $this->assertFalse($result['modifyingsubmissionallowed']);
265        $this->assertFalse($result['assessingallowed']);
266        $this->assertFalse($result['assessingexamplesallowed']);
267        $this->assertTrue($result['examplesassessedbeforesubmission']);
268        $this->assertTrue($result['examplesassessedbeforeassessment']);
269
270        // Switch phase.
271        $workshop = new workshop($this->workshop, $this->cm, $this->course);
272        $workshop->switch_phase(workshop::PHASE_SUBMISSION);
273        $result = mod_workshop_external::get_workshop_access_information($this->workshop->id);
274        $result = external_api::clean_returnvalue(mod_workshop_external::get_workshop_access_information_returns(), $result);
275
276        $this->assertTrue($result['creatingsubmissionallowed']);
277        $this->assertTrue($result['modifyingsubmissionallowed']);
278        $this->assertFalse($result['assessingallowed']);
279        $this->assertFalse($result['assessingexamplesallowed']);
280        $this->assertTrue($result['examplesassessedbeforesubmission']);
281        $this->assertTrue($result['examplesassessedbeforeassessment']);
282
283        // Switch to next (to assessment).
284        $workshop->switch_phase(workshop::PHASE_ASSESSMENT);
285        $result = mod_workshop_external::get_workshop_access_information($this->workshop->id);
286        $result = external_api::clean_returnvalue(mod_workshop_external::get_workshop_access_information_returns(), $result);
287
288        $this->assertFalse($result['creatingsubmissionallowed']);
289        $this->assertFalse($result['modifyingsubmissionallowed']);
290        $this->assertTrue($result['assessingallowed']);
291        $this->assertFalse($result['assessingexamplesallowed']);
292        $this->assertTrue($result['examplesassessedbeforesubmission']);
293        $this->assertTrue($result['examplesassessedbeforeassessment']);
294    }
295
296    /**
297     * Test mod_workshop_get_workshop_access_information for teachers.
298     */
299    public function test_mod_workshop_get_workshop_access_information_teacher() {
300
301        self::setUser($this->teacher);
302        $result = mod_workshop_external::get_workshop_access_information($this->workshop->id);
303        $result = external_api::clean_returnvalue(mod_workshop_external::get_workshop_access_information_returns(), $result);
304        // Check default values.
305        $disabledcaps = array('canpeerassess', 'cansubmit');
306
307        foreach ($result as $capname => $capvalue) {
308            if (strpos($capname, 'can') !== 0) {
309                continue;
310            }
311            if (in_array($capname, $disabledcaps)) {
312                $this->assertFalse($capvalue);
313            } else {
314                $this->assertTrue($capvalue);
315            }
316        }
317
318        // Now, specific functionalities.
319        $this->assertFalse($result['creatingsubmissionallowed']);
320        $this->assertFalse($result['modifyingsubmissionallowed']);
321        $this->assertFalse($result['assessingallowed']);
322        $this->assertFalse($result['assessingexamplesallowed']);
323    }
324
325    /**
326     * Test mod_workshop_get_user_plan for students.
327     */
328    public function test_mod_workshop_get_user_plan_student() {
329
330        self::setUser($this->student);
331        $result = mod_workshop_external::get_user_plan($this->workshop->id);
332        $result = external_api::clean_returnvalue(mod_workshop_external::get_user_plan_returns(), $result);
333
334        $this->assertCount(0, $result['userplan']['examples']);  // No examples given.
335        $this->assertCount(5, $result['userplan']['phases']);  // Always 5 phases.
336        $this->assertEquals(workshop::PHASE_SETUP, $result['userplan']['phases'][0]['code']);  // First phase always setup.
337        $this->assertTrue($result['userplan']['phases'][0]['active']); // First phase "Setup" active in new workshops.
338
339        // Switch phase.
340        $workshop = new workshop($this->workshop, $this->cm, $this->course);
341        $workshop->switch_phase(workshop::PHASE_SUBMISSION);
342
343        $result = mod_workshop_external::get_user_plan($this->workshop->id);
344        $result = external_api::clean_returnvalue(mod_workshop_external::get_user_plan_returns(), $result);
345
346        $this->assertEquals(workshop::PHASE_SUBMISSION, $result['userplan']['phases'][1]['code']);
347        $this->assertTrue($result['userplan']['phases'][1]['active']); // We are now in submission phase.
348    }
349
350    /**
351     * Test mod_workshop_get_user_plan for teachers.
352     */
353    public function test_mod_workshop_get_user_plan_teacher() {
354
355        self::setUser($this->teacher);
356        $result = mod_workshop_external::get_user_plan($this->workshop->id);
357        $result = external_api::clean_returnvalue(mod_workshop_external::get_user_plan_returns(), $result);
358
359        $this->assertCount(0, $result['userplan']['examples']);  // No examples given.
360        $this->assertCount(5, $result['userplan']['phases']);  // Always 5 phases.
361        $this->assertEquals(workshop::PHASE_SETUP, $result['userplan']['phases'][0]['code']);  // First phase always setup.
362        $this->assertTrue($result['userplan']['phases'][0]['active']); // First phase "Setup" active in new workshops.
363        $this->assertCount(4, $result['userplan']['phases'][0]['tasks']);  // For new empty workshops, always 4 tasks.
364
365        foreach ($result['userplan']['phases'][0]['tasks'] as $task) {
366            if ($task['code'] == 'intro' || $task['code'] == 'instructauthors' || $task['code'] == 'editform') {
367                $this->assertEquals(1, $task['completed']);
368            } else {
369                $this->assertEmpty($task['completed']);
370            }
371        }
372
373        // Do some of the tasks asked - switch phase.
374        $workshop = new workshop($this->workshop, $this->cm, $this->course);
375        $workshop->switch_phase(workshop::PHASE_SUBMISSION);
376
377        $result = mod_workshop_external::get_user_plan($this->workshop->id);
378        $result = external_api::clean_returnvalue(mod_workshop_external::get_user_plan_returns(), $result);
379        foreach ($result['userplan']['phases'][0]['tasks'] as $task) {
380            if ($task['code'] == 'intro' || $task['code'] == 'instructauthors' || $task['code'] == 'editform' ||
381                    $task['code'] == 'switchtonextphase') {
382                $this->assertEquals(1, $task['completed']);
383            } else {
384                $this->assertEmpty($task['completed']);
385            }
386        }
387
388        $result = mod_workshop_external::get_user_plan($this->workshop->id);
389        $result = external_api::clean_returnvalue(mod_workshop_external::get_user_plan_returns(), $result);
390
391        $this->assertEquals(workshop::PHASE_SUBMISSION, $result['userplan']['phases'][1]['code']);
392        $this->assertTrue($result['userplan']['phases'][1]['active']); // We are now in submission phase.
393    }
394
395    /**
396     * Test test_view_workshop invalid id.
397     */
398    public function test_view_workshop_invalid_id() {
399        $this->expectException('moodle_exception');
400        mod_workshop_external::view_workshop(0);
401    }
402
403    /**
404     * Test test_view_workshop user not enrolled.
405     */
406    public function test_view_workshop_user_not_enrolled() {
407        // Test not-enrolled user.
408        $usernotenrolled = self::getDataGenerator()->create_user();
409        $this->setUser($usernotenrolled);
410        $this->expectException('moodle_exception');
411        mod_workshop_external::view_workshop($this->workshop->id);
412    }
413
414    /**
415     * Test test_view_workshop user student.
416     */
417    public function test_view_workshop_user_student() {
418        // Test user with full capabilities.
419        $this->setUser($this->student);
420
421        // Trigger and capture the event.
422        $sink = $this->redirectEvents();
423
424        $result = mod_workshop_external::view_workshop($this->workshop->id);
425        $result = external_api::clean_returnvalue(mod_workshop_external::view_workshop_returns(), $result);
426        $this->assertTrue($result['status']);
427
428        $events = $sink->get_events();
429        $this->assertCount(1, $events);
430        $event = array_shift($events);
431
432        // Checking that the event contains the expected values.
433        $this->assertInstanceOf('\mod_workshop\event\course_module_viewed', $event);
434        $this->assertEquals($this->context, $event->get_context());
435        $moodleworkshop = new \moodle_url('/mod/workshop/view.php', array('id' => $this->cm->id));
436        $this->assertEquals($moodleworkshop, $event->get_url());
437        $this->assertEventContextNotUsed($event);
438        $this->assertNotEmpty($event->get_name());
439    }
440
441    /**
442     * Test test_view_workshop user missing capabilities.
443     */
444    public function test_view_workshop_user_missing_capabilities() {
445        // Test user with no capabilities.
446        // We need a explicit prohibit since this capability is only defined in authenticated user and guest roles.
447        assign_capability('mod/workshop:view', CAP_PROHIBIT, $this->studentrole->id, $this->context->id);
448        // Empty all the caches that may be affected  by this change.
449        accesslib_clear_all_caches_for_unit_testing();
450        course_modinfo::clear_instance_cache();
451
452        $this->setUser($this->student);
453        $this->expectException('moodle_exception');
454        mod_workshop_external::view_workshop($this->workshop->id);
455    }
456
457    /**
458     * Test test_add_submission.
459     */
460    public function test_add_submission() {
461        $fs = get_file_storage();
462
463        // Test user with full capabilities.
464        $this->setUser($this->student);
465
466        $title = 'Submission title';
467        $content = 'Submission contents';
468
469        // Create a file in a draft area for inline attachments.
470        $draftidinlineattach = file_get_unused_draft_itemid();
471        $usercontext = context_user::instance($this->student->id);
472        $filenameimg = 'shouldbeanimage.txt';
473        $filerecordinline = array(
474            'contextid' => $usercontext->id,
475            'component' => 'user',
476            'filearea'  => 'draft',
477            'itemid'    => $draftidinlineattach,
478            'filepath'  => '/',
479            'filename'  => $filenameimg,
480        );
481        $fs->create_file_from_string($filerecordinline, 'image contents (not really)');
482
483        // Create a file in a draft area for regular attachments.
484        $draftidattach = file_get_unused_draft_itemid();
485        $filerecordattach = $filerecordinline;
486        $attachfilename = 'attachment.txt';
487        $filerecordattach['filename'] = $attachfilename;
488        $filerecordattach['itemid'] = $draftidattach;
489        $fs->create_file_from_string($filerecordattach, 'simple text attachment');
490
491        // Switch to submission phase.
492        $workshop = new workshop($this->workshop, $this->cm, $this->course);
493        $workshop->switch_phase(workshop::PHASE_SUBMISSION);
494
495        $result = mod_workshop_external::add_submission($this->workshop->id, $title, $content, FORMAT_MOODLE, $draftidinlineattach,
496            $draftidattach);
497        $result = external_api::clean_returnvalue(mod_workshop_external::add_submission_returns(), $result);
498        $this->assertEmpty($result['warnings']);
499
500        // Check submission created.
501        $submission = $workshop->get_submission_by_author($this->student->id);
502        $this->assertTrue($result['status']);
503        $this->assertEquals($result['submissionid'], $submission->id);
504        $this->assertEquals($title, $submission->title);
505        $this->assertEquals($content, $submission->content);
506
507        // Check files.
508        $contentfiles = $fs->get_area_files($this->context->id, 'mod_workshop', 'submission_content', $submission->id);
509        $this->assertCount(2, $contentfiles);
510        foreach ($contentfiles as $file) {
511            if ($file->is_directory()) {
512                continue;
513            } else {
514                $this->assertEquals($filenameimg, $file->get_filename());
515            }
516        }
517        $contentfiles = $fs->get_area_files($this->context->id, 'mod_workshop', 'submission_attachment', $submission->id);
518        $this->assertCount(2, $contentfiles);
519        foreach ($contentfiles as $file) {
520            if ($file->is_directory()) {
521                continue;
522            } else {
523                $this->assertEquals($attachfilename, $file->get_filename());
524            }
525        }
526    }
527
528    /**
529     * Test test_add_submission invalid phase.
530     */
531    public function test_add_submission_invalid_phase() {
532        $this->setUser($this->student);
533
534        $this->expectException('moodle_exception');
535        mod_workshop_external::add_submission($this->workshop->id, 'Test');
536    }
537
538    /**
539     * Test test_add_submission empty title.
540     */
541    public function test_add_submission_empty_title() {
542        $this->setUser($this->student);
543
544        // Switch to submission phase.
545        $workshop = new workshop($this->workshop, $this->cm, $this->course);
546        $workshop->switch_phase(workshop::PHASE_SUBMISSION);
547
548        $this->expectException('moodle_exception');
549        mod_workshop_external::add_submission($this->workshop->id, '');
550    }
551
552    /**
553     * Test test_add_submission already added.
554     */
555    public function test_add_submission_already_added() {
556        $this->setUser($this->student);
557
558        $usercontext = context_user::instance($this->student->id);
559        $fs = get_file_storage();
560        $draftidattach = file_get_unused_draft_itemid();
561        $filerecordattach = [
562            'contextid' => $usercontext->id,
563            'component' => 'user',
564            'filearea'  => 'draft',
565            'itemid'    => $draftidattach,
566            'filepath'  => '/',
567            'filename'  => 'attachement.txt'
568        ];
569        $fs->create_file_from_string($filerecordattach, 'simple text attachment');
570
571        // Switch to submission phase.
572        $workshop = new workshop($this->workshop, $this->cm, $this->course);
573        $workshop->switch_phase(workshop::PHASE_SUBMISSION);
574
575        // Create the submission.
576        $result = mod_workshop_external::add_submission($this->workshop->id, 'My submission', '', FORMAT_MOODLE, 0, $draftidattach);
577        $result = external_api::clean_returnvalue(mod_workshop_external::add_submission_returns(), $result);
578
579        // Try to create it again.
580        $result = mod_workshop_external::add_submission($this->workshop->id, 'My submission', '', FORMAT_MOODLE, 0, $draftidattach);
581        $result = external_api::clean_returnvalue(mod_workshop_external::add_submission_returns(), $result);
582        $this->assertFalse($result['status']);
583        $this->assertArrayNotHasKey('submissionid', $result);
584        $this->assertCount(1, $result['warnings']);
585        $this->assertEquals('fielderror', $result['warnings'][0]['warningcode']);
586        $this->assertEquals('title', $result['warnings'][0]['item']);
587    }
588
589    /**
590     * Helper method to create a submission for testing for the given user.
591     *
592     * @param int $user the submission will be created by this student.
593     * @return int the submission id
594     */
595    protected function create_test_submission($user) {
596        // Test user with full capabilities.
597        $this->setUser($user);
598
599        $title = 'Submission title';
600        $content = 'Submission contents';
601
602        // Create a file in a draft area for inline attachments.
603        $fs = get_file_storage();
604        $draftidinlineattach = file_get_unused_draft_itemid();
605        $usercontext = context_user::instance($user->id);
606        $filenameimg = 'shouldbeanimage.txt';
607        $filerecordinline = array(
608            'contextid' => $usercontext->id,
609            'component' => 'user',
610            'filearea'  => 'draft',
611            'itemid'    => $draftidinlineattach,
612            'filepath'  => '/',
613            'filename'  => $filenameimg,
614        );
615        $fs->create_file_from_string($filerecordinline, 'image contents (not really)');
616
617        // Create a file in a draft area for regular attachments.
618        $draftidattach = file_get_unused_draft_itemid();
619        $filerecordattach = $filerecordinline;
620        $attachfilename = 'attachment.txt';
621        $filerecordattach['filename'] = $attachfilename;
622        $filerecordattach['itemid'] = $draftidattach;
623        $fs->create_file_from_string($filerecordattach, 'simple text attachment');
624
625        // Switch to submission phase.
626        $workshop = new workshop($this->workshop, $this->cm, $this->course);
627        $workshop->switch_phase(workshop::PHASE_SUBMISSION);
628
629        $result = mod_workshop_external::add_submission($this->workshop->id, $title, $content, FORMAT_MOODLE, $draftidinlineattach,
630            $draftidattach);
631        return $result['submissionid'];
632    }
633
634    /**
635     * Test test_update_submission.
636     */
637    public function test_update_submission() {
638
639        // Create the submission that will be updated.
640        $submissionid = $this->create_test_submission($this->student);
641
642        // Test user with full capabilities.
643        $this->setUser($this->student);
644
645        $title = 'Submission new title';
646        $content = 'Submission new contents';
647
648        // Create a different file in a draft area for inline attachments.
649        $fs = get_file_storage();
650        $draftidinlineattach = file_get_unused_draft_itemid();
651        $usercontext = context_user::instance($this->student->id);
652        $filenameimg = 'shouldbeanimage_new.txt';
653        $filerecordinline = array(
654            'contextid' => $usercontext->id,
655            'component' => 'user',
656            'filearea'  => 'draft',
657            'itemid'    => $draftidinlineattach,
658            'filepath'  => '/',
659            'filename'  => $filenameimg,
660        );
661        $fs->create_file_from_string($filerecordinline, 'image contents (not really)');
662
663        // Create a different file in a draft area for regular attachments.
664        $draftidattach = file_get_unused_draft_itemid();
665        $filerecordattach = $filerecordinline;
666        $attachfilename = 'attachment_new.txt';
667        $filerecordattach['filename'] = $attachfilename;
668        $filerecordattach['itemid'] = $draftidattach;
669        $fs->create_file_from_string($filerecordattach, 'simple text attachment');
670
671        $result = mod_workshop_external::update_submission($submissionid, $title, $content, FORMAT_MOODLE, $draftidinlineattach,
672            $draftidattach);
673        $result = external_api::clean_returnvalue(mod_workshop_external::update_submission_returns(), $result);
674        $this->assertEmpty($result['warnings']);
675
676        // Check submission updated.
677        $workshop = new workshop($this->workshop, $this->cm, $this->course);
678        $submission = $workshop->get_submission_by_id($submissionid);
679        $this->assertTrue($result['status']);
680        $this->assertEquals($title, $submission->title);
681        $this->assertEquals($content, $submission->content);
682
683        // Check files.
684        $contentfiles = $fs->get_area_files($this->context->id, 'mod_workshop', 'submission_content', $submission->id);
685        $this->assertCount(2, $contentfiles);
686        foreach ($contentfiles as $file) {
687            if ($file->is_directory()) {
688                continue;
689            } else {
690                $this->assertEquals($filenameimg, $file->get_filename());
691            }
692        }
693        $contentfiles = $fs->get_area_files($this->context->id, 'mod_workshop', 'submission_attachment', $submission->id);
694        $this->assertCount(2, $contentfiles);
695        foreach ($contentfiles as $file) {
696            if ($file->is_directory()) {
697                continue;
698            } else {
699                $this->assertEquals($attachfilename, $file->get_filename());
700            }
701        }
702    }
703
704    /**
705     * Test test_update_submission belonging to other user.
706     */
707    public function test_update_submission_of_other_user() {
708        // Create the submission that will be updated.
709        $submissionid = $this->create_test_submission($this->student);
710
711        $this->setUser($this->teacher);
712
713        $this->expectException('moodle_exception');
714        mod_workshop_external::update_submission($submissionid, 'Test');
715    }
716
717    /**
718     * Test test_update_submission invalid phase.
719     */
720    public function test_update_submission_invalid_phase() {
721        // Create the submission that will be updated.
722        $submissionid = $this->create_test_submission($this->student);
723
724        $this->setUser($this->student);
725
726        // Switch to assessment phase.
727        $workshop = new workshop($this->workshop, $this->cm, $this->course);
728        $workshop->switch_phase(workshop::PHASE_ASSESSMENT);
729
730        $this->expectException('moodle_exception');
731        mod_workshop_external::update_submission($submissionid, 'Test');
732    }
733
734    /**
735     * Test test_update_submission empty title.
736     */
737    public function test_update_submission_empty_title() {
738        // Create the submission that will be updated.
739        $submissionid = $this->create_test_submission($this->student);
740
741        $this->setUser($this->student);
742
743        $this->expectException('moodle_exception');
744        mod_workshop_external::update_submission($submissionid, '');
745    }
746
747    /**
748     * Test test_delete_submission.
749     */
750    public function test_delete_submission() {
751
752        // Create the submission that will be deleted.
753        $submissionid = $this->create_test_submission($this->student);
754
755        $this->setUser($this->student);
756
757        // Trigger and capture the event.
758        $sink = $this->redirectEvents();
759
760        $result = mod_workshop_external::delete_submission($submissionid);
761        $result = external_api::clean_returnvalue(mod_workshop_external::delete_submission_returns(), $result);
762        $this->assertEmpty($result['warnings']);
763        $this->assertTrue($result['status']);
764        $workshop = new workshop($this->workshop, $this->cm, $this->course);
765        $submission = $workshop->get_submission_by_author($this->student->id);
766        $this->assertFalse($submission);
767
768        $events = $sink->get_events();
769        $this->assertCount(1, $events);
770        $event = array_shift($events);
771
772        // Checking event.
773        $this->assertInstanceOf('\mod_workshop\event\submission_deleted', $event);
774        $this->assertEquals($this->context, $event->get_context());
775    }
776
777    /**
778     * Test test_delete_submission_with_assessments.
779     */
780    public function test_delete_submission_with_assessments() {
781
782        // Create the submission that will be deleted.
783        $submissionid = $this->create_test_submission($this->student);
784
785        $workshopgenerator = $this->getDataGenerator()->get_plugin_generator('mod_workshop');
786        $workshopgenerator->create_assessment($submissionid, $this->teacher->id, array(
787            'weight' => 3,
788            'grade' => 95.00000,
789        ));
790
791        $this->setUser($this->student);
792        $this->expectException('moodle_exception');
793        mod_workshop_external::delete_submission($submissionid);
794    }
795
796    /**
797     * Test test_delete_submission_invalid_phase.
798     */
799    public function test_delete_submission_invalid_phase() {
800
801        // Create the submission that will be deleted.
802        $submissionid = $this->create_test_submission($this->student);
803
804        // Switch to assessment phase.
805        $workshop = new workshop($this->workshop, $this->cm, $this->course);
806        $workshop->switch_phase(workshop::PHASE_ASSESSMENT);
807
808        $this->setUser($this->student);
809        $this->expectException('moodle_exception');
810        mod_workshop_external::delete_submission($submissionid);
811    }
812
813    /**
814     * Test test_delete_submission_as_teacher.
815     */
816    public function test_delete_submission_as_teacher() {
817
818        // Create the submission that will be deleted.
819        $submissionid = $this->create_test_submission($this->student);
820
821        $this->setUser($this->teacher);
822        $result = mod_workshop_external::delete_submission($submissionid);
823        $result = external_api::clean_returnvalue(mod_workshop_external::delete_submission_returns(), $result);
824        $this->assertEmpty($result['warnings']);
825        $this->assertTrue($result['status']);
826    }
827
828    /**
829     * Test test_delete_submission_other_user.
830     */
831    public function test_delete_submission_other_user() {
832
833        $anotheruser = self::getDataGenerator()->create_user();
834        $this->getDataGenerator()->enrol_user($anotheruser->id, $this->course->id, $this->studentrole->id, 'manual');
835        // Create the submission that will be deleted.
836        $submissionid = $this->create_test_submission($this->student);
837
838        $this->setUser($anotheruser);
839        $this->expectException('moodle_exception');
840        mod_workshop_external::delete_submission($submissionid);
841    }
842
843    /**
844     * Test test_get_submissions_student.
845     */
846    public function test_get_submissions_student() {
847
848        // Create a couple of submissions with files.
849        $firstsubmissionid = $this->create_test_submission($this->student);  // Create submission with files.
850        $secondsubmissionid = $this->create_test_submission($this->anotherstudentg1);
851
852        $this->setUser($this->student);
853        $result = mod_workshop_external::get_submissions($this->workshop->id);
854        $result = external_api::clean_returnvalue(mod_workshop_external::get_submissions_returns(), $result);
855        // We should get just our submission.
856        $this->assertCount(1, $result['submissions']);
857        $this->assertEquals(1, $result['totalcount']);
858        $this->assertEquals($firstsubmissionid, $result['submissions'][0]['id']);
859        $this->assertCount(1, $result['submissions'][0]['contentfiles']); // Check we retrieve submission text files.
860        $this->assertCount(1, $result['submissions'][0]['attachmentfiles']); // Check we retrieve attachment files.
861        // We shoul not see the grade or feedback information.
862        $properties = submission_exporter::properties_definition();
863        foreach ($properties as $attribute => $settings) {
864            if (!empty($settings['optional'])) {
865                if (isset($result['submissions'][0][$attribute])) {
866                    echo "error $attribute";
867                }
868                $this->assertFalse(isset($result['submissions'][0][$attribute]));
869            }
870        }
871    }
872
873    /**
874     * Test test_get_submissions_published_student.
875     */
876    public function test_get_submissions_published_student() {
877
878        $workshop = new workshop($this->workshop, $this->cm, $this->course);
879        $workshop->switch_phase(workshop::PHASE_CLOSED);
880        // Create a couple of submissions with files.
881        $workshopgenerator = $this->getDataGenerator()->get_plugin_generator('mod_workshop');
882        $submission = array('published' => 1);
883        $submissionid = $workshopgenerator->create_submission($this->workshop->id, $this->anotherstudentg1->id, $submission);
884
885        $this->setUser($this->student);
886        $result = mod_workshop_external::get_submissions($this->workshop->id);
887        $result = external_api::clean_returnvalue(mod_workshop_external::get_submissions_returns(), $result);
888        // We should get just our submission.
889        $this->assertCount(1, $result['submissions']);
890        $this->assertEquals(1, $result['totalcount']);
891        $this->assertEquals($submissionid, $result['submissions'][0]['id']);
892
893        // Check with group restrictions.
894        $this->setUser($this->anotherstudentg2);
895        $result = mod_workshop_external::get_submissions($this->workshop->id);
896        $result = external_api::clean_returnvalue(mod_workshop_external::get_submissions_returns(), $result);
897        $this->assertCount(0, $result['submissions']);  // I can't see other users in separated groups.
898        $this->assertEquals(0, $result['totalcount']);
899    }
900
901    /**
902     * Test test_get_submissions_from_student_with_feedback_from_teacher.
903     */
904    public function test_get_submissions_from_student_with_feedback_from_teacher() {
905        global $DB;
906
907        // Create a couple of submissions with files.
908        $workshopgenerator = $this->getDataGenerator()->get_plugin_generator('mod_workshop');
909        $submissionid = $workshopgenerator->create_submission($this->workshop->id, $this->student->id);
910        // Create teacher feedback for submission.
911        $record = new stdclass();
912        $record->id = $submissionid;
913        $record->gradeover = 9;
914        $record->gradeoverby = $this->teacher->id;
915        $record->feedbackauthor = 'Hey';
916        $record->feedbackauthorformat = FORMAT_MOODLE;
917        $record->published = 1;
918        $DB->update_record('workshop_submissions', $record);
919
920        // Remove teacher caps.
921        assign_capability('mod/workshop:viewallsubmissions', CAP_PROHIBIT, $this->teacher->id, $this->context->id);
922        // Empty all the caches that may be affected  by this change.
923        accesslib_clear_all_caches_for_unit_testing();
924        course_modinfo::clear_instance_cache();
925
926        $this->setUser($this->teacher);
927        $result = mod_workshop_external::get_submissions($this->workshop->id, $this->student->id);
928        $result = external_api::clean_returnvalue(mod_workshop_external::get_submissions_returns(), $result);
929        // We should get just our submission.
930        $this->assertEquals(1, $result['totalcount']);
931        $this->assertEquals($submissionid, $result['submissions'][0]['id']);
932    }
933
934    /**
935     * Test test_get_submissions_from_students_as_teacher.
936     */
937    public function test_get_submissions_from_students_as_teacher() {
938
939        // Create a couple of submissions with files.
940        $workshopgenerator = $this->getDataGenerator()->get_plugin_generator('mod_workshop');
941        $submissionid1 = $workshopgenerator->create_submission($this->workshop->id, $this->student->id);
942        $submissionid2 = $workshopgenerator->create_submission($this->workshop->id, $this->anotherstudentg1->id);
943        $submissionid3 = $workshopgenerator->create_submission($this->workshop->id, $this->anotherstudentg2->id);
944
945        $this->setUser($this->teacher);
946        $result = mod_workshop_external::get_submissions($this->workshop->id); // Get all.
947        $result = external_api::clean_returnvalue(mod_workshop_external::get_submissions_returns(), $result);
948        $this->assertEquals(3, $result['totalcount']);
949        $this->assertCount(3, $result['submissions']);
950
951        $result = mod_workshop_external::get_submissions($this->workshop->id, 0, 0, 0, 2); // Check pagination.
952        $result = external_api::clean_returnvalue(mod_workshop_external::get_submissions_returns(), $result);
953        $this->assertEquals(3, $result['totalcount']);
954        $this->assertCount(2, $result['submissions']);
955
956        $result = mod_workshop_external::get_submissions($this->workshop->id, 0, $this->group2->id); // Get group 2.
957        $result = external_api::clean_returnvalue(mod_workshop_external::get_submissions_returns(), $result);
958        $this->assertEquals(1, $result['totalcount']);
959        $this->assertCount(1, $result['submissions']);
960        $this->assertEquals($submissionid3, $result['submissions'][0]['id']);
961
962        $result = mod_workshop_external::get_submissions($this->workshop->id, $this->anotherstudentg1->id); // Get one.
963        $result = external_api::clean_returnvalue(mod_workshop_external::get_submissions_returns(), $result);
964        $this->assertEquals(1, $result['totalcount']);
965        $this->assertEquals($submissionid2, $result['submissions'][0]['id']);
966    }
967
968    /**
969     * Test test_get_submission_student.
970     */
971    public function test_get_submission_student() {
972
973        // Create a couple of submissions with files.
974        $firstsubmissionid = $this->create_test_submission($this->student);  // Create submission with files.
975
976        $workshop = new workshop($this->workshop, $this->cm, $this->course);
977        $workshop->switch_phase(workshop::PHASE_CLOSED);
978        $this->setUser($this->student);
979        $result = mod_workshop_external::get_submission($firstsubmissionid);
980        $result = external_api::clean_returnvalue(mod_workshop_external::get_submission_returns(), $result);
981        $this->assertEquals($firstsubmissionid, $result['submission']['id']);
982        $this->assertCount(1, $result['submission']['contentfiles']); // Check we retrieve submission text files.
983        $this->assertCount(1, $result['submission']['attachmentfiles']); // Check we retrieve attachment files.
984        $this->assertArrayHasKey('feedbackauthor', $result['submission']);
985        $this->assertArrayNotHasKey('grade', $result['submission']);
986        $this->assertArrayNotHasKey('gradeover', $result['submission']);
987        $this->assertArrayHasKey('gradeoverby', $result['submission']);
988        $this->assertArrayNotHasKey('timegraded', $result['submission']);
989
990        // Switch to a different phase (where feedback won't be available).
991        $workshop->switch_phase(workshop::PHASE_EVALUATION);
992        $result = mod_workshop_external::get_submission($firstsubmissionid);
993        $result = external_api::clean_returnvalue(mod_workshop_external::get_submission_returns(), $result);
994        $this->assertEquals($firstsubmissionid, $result['submission']['id']);
995        $this->assertCount(1, $result['submission']['contentfiles']); // Check we retrieve submission text files.
996        $this->assertCount(1, $result['submission']['attachmentfiles']); // Check we retrieve attachment files.
997        $this->assertArrayNotHasKey('feedbackauthor', $result['submission']);
998        $this->assertArrayNotHasKey('grade', $result['submission']);
999        $this->assertArrayNotHasKey('gradeover', $result['submission']);
1000        $this->assertArrayNotHasKey('gradeoverby', $result['submission']);
1001        $this->assertArrayNotHasKey('timegraded', $result['submission']);
1002    }
1003
1004    /**
1005     * Test test_get_submission_i_reviewed.
1006     */
1007    public function test_get_submission_i_reviewed() {
1008
1009        // Create a couple of submissions with files.
1010        $firstsubmissionid = $this->create_test_submission($this->student);  // Create submission with files.
1011        $workshopgenerator = $this->getDataGenerator()->get_plugin_generator('mod_workshop');
1012        $workshopgenerator->create_assessment($firstsubmissionid, $this->anotherstudentg1->id, array(
1013            'weight' => 3,
1014            'grade' => 95,
1015        ));
1016        // Now try to get the submission I just reviewed.
1017        $this->setUser($this->anotherstudentg1);
1018        $result = mod_workshop_external::get_submission($firstsubmissionid);
1019        $result = external_api::clean_returnvalue(mod_workshop_external::get_submission_returns(), $result);
1020        $this->assertEquals($firstsubmissionid, $result['submission']['id']);
1021        $this->assertCount(1, $result['submission']['contentfiles']); // Check we retrieve submission text files.
1022        $this->assertCount(1, $result['submission']['attachmentfiles']); // Check we retrieve attachment files.
1023        $this->assertArrayNotHasKey('feedbackauthor', $result['submission']);
1024        $this->assertArrayNotHasKey('grade', $result['submission']);
1025        $this->assertArrayNotHasKey('gradeover', $result['submission']);
1026        $this->assertArrayNotHasKey('gradeoverby', $result['submission']);
1027        $this->assertArrayNotHasKey('timegraded', $result['submission']);
1028    }
1029
1030    /**
1031     * Test test_get_submission_other_student.
1032     */
1033    public function test_get_submission_other_student() {
1034
1035        // Create a couple of submissions with files.
1036        $firstsubmissionid = $this->create_test_submission($this->student);  // Create submission with files.
1037        // Expect failure.
1038        $this->setUser($this->anotherstudentg1);
1039        $this->expectException('moodle_exception');
1040        $result = mod_workshop_external::get_submission($firstsubmissionid);
1041    }
1042
1043    /**
1044     * Test test_get_submission_published_student.
1045     */
1046    public function test_get_submission_published_student() {
1047
1048        $workshop = new workshop($this->workshop, $this->cm, $this->course);
1049        $workshop->switch_phase(workshop::PHASE_CLOSED);
1050        // Create a couple of submissions with files.
1051        $workshopgenerator = $this->getDataGenerator()->get_plugin_generator('mod_workshop');
1052        $submission = array('published' => 1);
1053        $submissionid = $workshopgenerator->create_submission($this->workshop->id, $this->anotherstudentg1->id, $submission);
1054
1055        $this->setUser($this->student);
1056        $result = mod_workshop_external::get_submission($submissionid);
1057        $result = external_api::clean_returnvalue(mod_workshop_external::get_submission_returns(), $result);
1058        $this->assertEquals($submissionid, $result['submission']['id']);
1059        // Check that the student don't see the other student grade/feedback data even if is published.
1060        // We should not see the grade or feedback information.
1061        $properties = submission_exporter::properties_definition();
1062        $this->assertArrayNotHasKey('feedbackauthor', $result['submission']);
1063        $this->assertArrayNotHasKey('grade', $result['submission']);
1064        $this->assertArrayNotHasKey('gradeover', $result['submission']);
1065        $this->assertArrayNotHasKey('gradeoverby', $result['submission']);
1066        $this->assertArrayNotHasKey('timegraded', $result['submission']);
1067
1068        // Check with group restrictions.
1069        $this->setUser($this->anotherstudentg2);
1070        $this->expectException('moodle_exception');
1071        mod_workshop_external::get_submission($submissionid);
1072    }
1073
1074    /**
1075     * Test test_get_submission_from_student_with_feedback_from_teacher.
1076     */
1077    public function test_get_submission_from_student_with_feedback_from_teacher() {
1078        global $DB;
1079
1080        // Create a couple of submissions with files.
1081        $workshopgenerator = $this->getDataGenerator()->get_plugin_generator('mod_workshop');
1082        $submissionid = $workshopgenerator->create_submission($this->workshop->id, $this->student->id);
1083        $workshop = new workshop($this->workshop, $this->cm, $this->course);
1084        $workshop->switch_phase(workshop::PHASE_CLOSED);
1085        // Create teacher feedback for submission.
1086        $record = new stdclass();
1087        $record->id = $submissionid;
1088        $record->gradeover = 9;
1089        $record->gradeoverby = $this->teacher->id;
1090        $record->feedbackauthor = 'Hey';
1091        $record->feedbackauthorformat = FORMAT_MOODLE;
1092        $record->published = 1;
1093        $record->timegraded = time();
1094        $DB->update_record('workshop_submissions', $record);
1095
1096        $this->setUser($this->teacher);
1097        $result = mod_workshop_external::get_submission($submissionid);
1098        $result = external_api::clean_returnvalue(mod_workshop_external::get_submission_returns(), $result);
1099        $this->assertEquals($submissionid, $result['submission']['id']);
1100        $this->assertEquals($record->feedbackauthor, $result['submission']['feedbackauthor']);
1101        $this->assertEquals($record->gradeover, $result['submission']['gradeover']);
1102        $this->assertEquals($record->gradeoverby, $result['submission']['gradeoverby']);
1103        $this->assertEquals($record->timegraded, $result['submission']['timegraded']);
1104
1105        // Go to phase where feedback and grades are not yet available.
1106        $workshop->switch_phase(workshop::PHASE_SUBMISSION);
1107        $result = mod_workshop_external::get_submission($submissionid);
1108        $result = external_api::clean_returnvalue(mod_workshop_external::get_submission_returns(), $result);
1109        $this->assertArrayNotHasKey('feedbackauthor', $result['submission']);
1110        $this->assertArrayNotHasKey('grade', $result['submission']);
1111        $this->assertArrayNotHasKey('gradeover', $result['submission']);
1112        $this->assertArrayNotHasKey('gradeoverby', $result['submission']);
1113        $this->assertArrayNotHasKey('timegraded', $result['submission']);
1114
1115        // Remove teacher caps to view and go to valid phase.
1116        $workshop->switch_phase(workshop::PHASE_EVALUATION);
1117        unassign_capability('mod/workshop:viewallsubmissions', $this->teacherrole->id);
1118        // Empty all the caches that may be affected  by this change.
1119        accesslib_clear_all_caches_for_unit_testing();
1120
1121        $this->expectException('moodle_exception');
1122        mod_workshop_external::get_submission($submissionid);
1123    }
1124
1125    /**
1126     * Test test_get_submission_from_students_as_teacher.
1127     */
1128    public function test_get_submission_from_students_as_teacher() {
1129        // Create a couple of submissions with files.
1130        $workshopgenerator = $this->getDataGenerator()->get_plugin_generator('mod_workshop');
1131        $submissionid1 = $workshopgenerator->create_submission($this->workshop->id, $this->student->id);
1132        $submissionid2 = $workshopgenerator->create_submission($this->workshop->id, $this->anotherstudentg1->id);
1133        $submissionid3 = $workshopgenerator->create_submission($this->workshop->id, $this->anotherstudentg2->id);
1134
1135        $this->setUser($this->teacher);
1136        $result = mod_workshop_external::get_submission($submissionid1); // Get all.
1137        $result = external_api::clean_returnvalue(mod_workshop_external::get_submission_returns(), $result);
1138        $this->assertEquals($submissionid1, $result['submission']['id']);
1139
1140        $result = mod_workshop_external::get_submission($submissionid3); // Get group 2.
1141        $result = external_api::clean_returnvalue(mod_workshop_external::get_submission_returns(), $result);
1142        $this->assertEquals($submissionid3, $result['submission']['id']);
1143    }
1144
1145
1146    /**
1147     * Test get_submission_assessments_student.
1148     */
1149    public function test_get_submission_assessments_student() {
1150
1151        // Create the submission that will be deleted.
1152        $submissionid = $this->create_test_submission($this->student);
1153
1154        $workshopgenerator = $this->getDataGenerator()->get_plugin_generator('mod_workshop');
1155        $workshopgenerator->create_assessment($submissionid, $this->anotherstudentg1->id, array(
1156            'weight' => 3,
1157            'grade' => 95,
1158        ));
1159        $workshopgenerator->create_assessment($submissionid, $this->student->id, array(
1160            'weight' => 2,
1161            'grade' => 90,
1162        ));
1163
1164        $workshop = new workshop($this->workshop, $this->cm, $this->course);
1165        $workshop->switch_phase(workshop::PHASE_CLOSED);
1166        $this->setUser($this->student);
1167        $result = mod_workshop_external::get_submission_assessments($submissionid);
1168        $result = external_api::clean_returnvalue(mod_workshop_external::get_submission_assessments_returns(), $result);
1169        $this->assertCount(2, $result['assessments']);  // I received my two assessments.
1170        foreach ($result['assessments'] as $assessment) {
1171            if ($assessment['grade'] == 90) {
1172                // My own assessment, I can see me.
1173                $this->assertEquals($this->student->id, $assessment['reviewerid']);
1174            } else {
1175                // Student's can't see who did the review.
1176                $this->assertEquals(0, $assessment['reviewerid']);
1177            }
1178        }
1179    }
1180
1181    /**
1182     * Test get_submission_assessments_invalid_phase.
1183     */
1184    public function test_get_submission_assessments_invalid_phase() {
1185
1186        // Create the submission that will be deleted.
1187        $submissionid = $this->create_test_submission($this->student);
1188
1189        $workshopgenerator = $this->getDataGenerator()->get_plugin_generator('mod_workshop');
1190        $workshopgenerator->create_assessment($submissionid, $this->anotherstudentg1->id, array(
1191            'weight' => 3,
1192            'grade' => 95,
1193        ));
1194
1195        $this->expectException('moodle_exception');
1196        mod_workshop_external::get_submission_assessments($submissionid);
1197    }
1198
1199    /**
1200     * Test get_submission_assessments_teacher.
1201     */
1202    public function test_get_submission_assessments_teacher() {
1203
1204        // Create the submission that will be deleted.
1205        $submissionid = $this->create_test_submission($this->student);
1206
1207        $workshopgenerator = $this->getDataGenerator()->get_plugin_generator('mod_workshop');
1208        $assessmentid = $workshopgenerator->create_assessment($submissionid, $this->anotherstudentg1->id, array(
1209            'weight' => 1,
1210            'grade' => 50,
1211        ));
1212
1213        $this->setUser($this->teacher);
1214        $result = mod_workshop_external::get_submission_assessments($submissionid);
1215        $result = external_api::clean_returnvalue(mod_workshop_external::get_submission_assessments_returns(), $result);
1216        $this->assertCount(1, $result['assessments']);
1217        $this->assertEquals(50, $result['assessments'][0]['grade']);
1218        $this->assertEquals($assessmentid, $result['assessments'][0]['id']);
1219    }
1220
1221    /**
1222     * Test get_assessment_author.
1223     */
1224    public function test_get_assessment_author() {
1225
1226        // Create the submission.
1227        $submissionid = $this->create_test_submission($this->anotherstudentg1);
1228
1229        $workshopgenerator = $this->getDataGenerator()->get_plugin_generator('mod_workshop');
1230        $assessmentid = $workshopgenerator->create_assessment($submissionid, $this->student->id, array(
1231            'weight' => 2,
1232            'grade' => 90,
1233        ));
1234
1235        // Switch to closed phase.
1236        $workshop = new workshop($this->workshop, $this->cm, $this->course);
1237        $workshop->switch_phase(workshop::PHASE_CLOSED);
1238        $this->setUser($this->anotherstudentg1);
1239        $result = mod_workshop_external::get_assessment($assessmentid);
1240        $result = external_api::clean_returnvalue(mod_workshop_external::get_assessment_returns(), $result);
1241        $this->assertEquals($assessmentid, $result['assessment']['id']);
1242        $this->assertEquals(90, $result['assessment']['grade']);
1243        // I can't see the reviewer review.
1244        $this->assertFalse(isset($result['assessment']['feedbackreviewer']));
1245    }
1246
1247    /**
1248     * Test get_assessment_reviewer.
1249     */
1250    public function test_get_assessment_reviewer() {
1251
1252        // Create the submission.
1253        $submissionid = $this->create_test_submission($this->anotherstudentg1);
1254
1255        $workshopgenerator = $this->getDataGenerator()->get_plugin_generator('mod_workshop');
1256        $assessmentid = $workshopgenerator->create_assessment($submissionid, $this->student->id, array(
1257            'weight' => 2,
1258            'grade' => 90,
1259        ));
1260
1261        // Switch to closed phase.
1262        $workshop = new workshop($this->workshop, $this->cm, $this->course);
1263        $workshop->switch_phase(workshop::PHASE_CLOSED);
1264        $this->setUser($this->student);
1265        $result = mod_workshop_external::get_assessment($assessmentid);
1266        $result = external_api::clean_returnvalue(mod_workshop_external::get_assessment_returns(), $result);
1267        $this->assertEquals($assessmentid, $result['assessment']['id']);
1268        $this->assertEquals(90, $result['assessment']['grade']);
1269        // I can see the reviewer review.
1270        $this->assertTrue(isset($result['assessment']['feedbackreviewer']));
1271    }
1272
1273    /**
1274     * Test get_assessment_teacher.
1275     */
1276    public function test_get_assessment_teacher() {
1277
1278        // Create the submission.
1279        $submissionid = $this->create_test_submission($this->anotherstudentg1);
1280
1281        $workshopgenerator = $this->getDataGenerator()->get_plugin_generator('mod_workshop');
1282        $assessmentid = $workshopgenerator->create_assessment($submissionid, $this->student->id, array(
1283            'weight' => 2,
1284            'grade' => 90,
1285        ));
1286
1287        // Switch to closed phase.
1288        $workshop = new workshop($this->workshop, $this->cm, $this->course);
1289        $workshop->switch_phase(workshop::PHASE_CLOSED);
1290        $this->setUser($this->teacher);
1291        $result = mod_workshop_external::get_assessment($assessmentid);
1292        $result = external_api::clean_returnvalue(mod_workshop_external::get_assessment_returns(), $result);
1293        $this->assertEquals($assessmentid, $result['assessment']['id']);
1294        $this->assertEquals(90, $result['assessment']['grade']);
1295    }
1296
1297    /**
1298     * Test get_assessment_student_invalid_phase.
1299     */
1300    public function test_get_assessment_student_invalid_phase() {
1301
1302        // Create the submission.
1303        $submissionid = $this->create_test_submission($this->anotherstudentg1);
1304
1305        $workshopgenerator = $this->getDataGenerator()->get_plugin_generator('mod_workshop');
1306        $assessmentid = $workshopgenerator->create_assessment($submissionid, $this->student->id, array(
1307            'weight' => 2,
1308            'grade' => 90,
1309        ));
1310
1311        // Switch to closed phase.
1312        $this->setUser($this->anotherstudentg1);
1313
1314        $this->expectException('moodle_exception');
1315        mod_workshop_external::get_assessment($assessmentid);
1316    }
1317
1318    /**
1319     * Test get_assessment_student_invalid_user.
1320     */
1321    public function test_get_assessment_student_invalid_user() {
1322
1323        // Create the submission.
1324        $submissionid = $this->create_test_submission($this->anotherstudentg1);
1325
1326        $workshopgenerator = $this->getDataGenerator()->get_plugin_generator('mod_workshop');
1327        $assessmentid = $workshopgenerator->create_assessment($submissionid, $this->student->id, array(
1328            'weight' => 2,
1329            'grade' => 90,
1330        ));
1331
1332        // Switch to closed phase.
1333        $workshop = new workshop($this->workshop, $this->cm, $this->course);
1334        $workshop->switch_phase(workshop::PHASE_CLOSED);
1335        $this->setUser($this->anotherstudentg2);
1336
1337        $this->expectException('moodle_exception');
1338        mod_workshop_external::get_assessment($assessmentid);
1339    }
1340
1341    /**
1342     * Test get_assessment_form_definition_reviewer_new_assessment.
1343     */
1344    public function test_get_assessment_form_definition_reviewer_new_assessment() {
1345
1346        // Create the submission.
1347        $submissionid = $this->create_test_submission($this->anotherstudentg1);
1348
1349        $workshop = new workshop($this->workshop, $this->cm, $this->course);
1350        $submission = $workshop->get_submission_by_id($submissionid);
1351        $assessmentid = $workshop->add_allocation($submission, $this->student->id);
1352
1353        // Switch to assessment phase.
1354        $workshop->switch_phase(workshop::PHASE_ASSESSMENT);
1355        $this->setUser($this->student);
1356        $result = mod_workshop_external::get_assessment_form_definition($assessmentid);
1357        $result = external_api::clean_returnvalue(mod_workshop_external::get_assessment_form_definition_returns(), $result);
1358        $this->assertEquals(4, $result['dimenssionscount']);    // We receive the expected 4 dimensions.
1359        $this->assertEmpty($result['current']); // Assessment not yet done.
1360        foreach ($result['fields'] as $field) {
1361            if (strpos($field['name'], 'grade__idx_') === 0) {
1362                $this->assertEquals(25, $field['value']); // Check one of the dimension fields attributes.
1363            }
1364        }
1365        // Check dimensions grading info.
1366        foreach ($result['dimensionsinfo'] as $dimension) {
1367            $this->assertEquals(0, $dimension['min']);
1368            $this->assertEquals(25, $dimension['max']);
1369            $this->assertEquals(25, $dimension['weight']);
1370            $this->assertFalse(isset($dimension['scale']));
1371        }
1372    }
1373
1374    /**
1375     * Test get_assessment_form_definition_teacher_new_assessment.
1376     */
1377    public function test_get_assessment_form_definition_teacher_new_assessment() {
1378
1379        // Create the submission.
1380        $submissionid = $this->create_test_submission($this->anotherstudentg1);
1381
1382        $workshop = new workshop($this->workshop, $this->cm, $this->course);
1383        $submission = $workshop->get_submission_by_id($submissionid);
1384        $assessmentid = $workshop->add_allocation($submission, $this->student->id);
1385
1386        // Switch to assessment phase.
1387        $workshop->switch_phase(workshop::PHASE_ASSESSMENT);
1388        // Teachers need to be able to view assessments.
1389        $this->setUser($this->teacher);
1390        $result = mod_workshop_external::get_assessment_form_definition($assessmentid);
1391        $result = external_api::clean_returnvalue(mod_workshop_external::get_assessment_form_definition_returns(), $result);
1392        $this->assertEquals(4, $result['dimenssionscount']);
1393    }
1394
1395    /**
1396     * Test get_assessment_form_definition_invalid_phase.
1397     */
1398    public function test_get_assessment_form_definition_invalid_phase() {
1399
1400        // Create the submission.
1401        $submissionid = $this->create_test_submission($this->anotherstudentg1);
1402
1403        $workshop = new workshop($this->workshop, $this->cm, $this->course);
1404        $submission = $workshop->get_submission_by_id($submissionid);
1405        $assessmentid = $workshop->add_allocation($submission, $this->anotherstudentg1->id);
1406
1407        $workshop->switch_phase(workshop::PHASE_EVALUATION);
1408        $this->setUser($this->student);
1409        // Since we are not reviewers we can't see the assessment until the workshop is closed.
1410        $this->expectException('moodle_exception');
1411        mod_workshop_external::get_assessment_form_definition($assessmentid);
1412    }
1413
1414    /**
1415     * Test get_reviewer_assessments.
1416     */
1417    public function test_get_reviewer_assessments() {
1418
1419        // Create the submission.
1420        $submissionid1 = $this->create_test_submission($this->student);
1421        $submissionid2 = $this->create_test_submission($this->anotherstudentg1);
1422
1423        $workshopgenerator = $this->getDataGenerator()->get_plugin_generator('mod_workshop');
1424        $assessmentid1 = $workshopgenerator->create_assessment($submissionid1, $this->student->id, array(
1425            'weight' => 2,
1426            'grade' => 90,
1427        ));
1428        $assessmentid2 = $workshopgenerator->create_assessment($submissionid2, $this->student->id, array(
1429            'weight' => 3,
1430            'grade' => 80,
1431        ));
1432
1433        // Switch to assessment phase.
1434        $workshop = new workshop($this->workshop, $this->cm, $this->course);
1435        $workshop->switch_phase(workshop::PHASE_ASSESSMENT);
1436        $this->setUser($this->student);
1437        // Get my assessments.
1438        $result = mod_workshop_external::get_reviewer_assessments($this->workshop->id);
1439        $result = external_api::clean_returnvalue(mod_workshop_external::get_reviewer_assessments_returns(), $result);
1440        $this->assertCount(2, $result['assessments']);
1441        foreach ($result['assessments'] as $assessment) {
1442            if ($assessment['id'] == $assessmentid1) {
1443                $this->assertEquals(90, $assessment['grade']);
1444            } else {
1445                $this->assertEquals($assessmentid2, $assessment['id']);
1446                $this->assertEquals(80, $assessment['grade']);
1447            }
1448        }
1449
1450        // Now, as teacher try to get the same student assessments.
1451        $result = mod_workshop_external::get_reviewer_assessments($this->workshop->id, $this->student->id);
1452        $result = external_api::clean_returnvalue(mod_workshop_external::get_reviewer_assessments_returns(), $result);
1453        $this->assertCount(2, $result['assessments']);
1454        $this->assertArrayNotHasKey('feedbackreviewer', $result['assessments'][0]);
1455    }
1456
1457    /**
1458     * Test get_reviewer_assessments_other_student.
1459     */
1460    public function test_get_reviewer_assessments_other_student() {
1461
1462        $workshop = new workshop($this->workshop, $this->cm, $this->course);
1463        $workshop->switch_phase(workshop::PHASE_ASSESSMENT);
1464        // Try to get other user assessments.
1465        $this->setUser($this->student);
1466        $this->expectException('moodle_exception');
1467        mod_workshop_external::get_reviewer_assessments($this->workshop->id, $this->anotherstudentg1->id);
1468    }
1469
1470    /**
1471     * Test get_reviewer_assessments_invalid_phase.
1472     */
1473    public function test_get_reviewer_assessments_invalid_phase() {
1474
1475        $workshop = new workshop($this->workshop, $this->cm, $this->course);
1476        $workshop->switch_phase(workshop::PHASE_SUBMISSION);
1477        // Try to get other user assessments.
1478        $this->setUser($this->student);
1479        $this->expectException('moodle_exception');
1480        mod_workshop_external::get_reviewer_assessments($this->workshop->id, $this->anotherstudentg1->id);
1481    }
1482
1483    /**
1484     * Test update_assessment.
1485     */
1486    public function test_update_assessment() {
1487
1488        // Create the submission.
1489        $submissionid = $this->create_test_submission($this->anotherstudentg1);
1490
1491        $workshop = new workshop($this->workshop, $this->cm, $this->course);
1492        $submission = $workshop->get_submission_by_id($submissionid);
1493        $assessmentid = $workshop->add_allocation($submission, $this->student->id);
1494
1495        // Switch to assessment phase.
1496        $workshop->switch_phase(workshop::PHASE_ASSESSMENT);
1497        $this->setUser($this->student);
1498        // Get the form definition.
1499        $result = mod_workshop_external::get_assessment_form_definition($assessmentid);
1500        $result = external_api::clean_returnvalue(mod_workshop_external::get_assessment_form_definition_returns(), $result);
1501
1502        // Prepare the data to be sent.
1503        $data = $result['fields'];
1504        foreach ($data as $key => $param) {
1505            if (strpos($param['name'], 'peercomment__idx_') === 0) {
1506                $data[$key]['value'] = 'Some content';
1507            } else if (strpos($param['name'], 'grade__idx_') === 0) {
1508                $data[$key]['value'] = 25; // Set all to 25.
1509            }
1510        }
1511
1512        // Required data.
1513        $data[] = array(
1514            'name' => 'nodims',
1515            'value' => $result['dimenssionscount'],
1516        );
1517
1518        // General feedback.
1519        $data[] = array(
1520            'name' => 'feedbackauthor',
1521            'value' => 'Feedback for the author',
1522        );
1523        $data[] = array(
1524            'name' => 'feedbackauthorformat',
1525            'value' => FORMAT_MOODLE,
1526        );
1527
1528        // Create a file in a draft area for inline attachments.
1529        $fs = get_file_storage();
1530        $draftidinlineattach = file_get_unused_draft_itemid();
1531        $usercontext = context_user::instance($this->student->id);
1532        $filenameimg = 'shouldbeanimage.txt';
1533        $filerecordinline = array(
1534            'contextid' => $usercontext->id,
1535            'component' => 'user',
1536            'filearea'  => 'draft',
1537            'itemid'    => $draftidinlineattach,
1538            'filepath'  => '/',
1539            'filename'  => $filenameimg,
1540        );
1541        $fs->create_file_from_string($filerecordinline, 'image contents (not really)');
1542
1543        // Create a file in a draft area for regular attachments.
1544        $draftidattach = file_get_unused_draft_itemid();
1545        $filerecordattach = $filerecordinline;
1546        $attachfilename = 'attachment.txt';
1547        $filerecordattach['filename'] = $attachfilename;
1548        $filerecordattach['itemid'] = $draftidattach;
1549        $fs->create_file_from_string($filerecordattach, 'simple text attachment');
1550
1551        $data[] = array(
1552            'name' => 'feedbackauthorinlineattachmentsid',
1553            'value' => $draftidinlineattach,
1554        );
1555        $data[] = array(
1556            'name' => 'feedbackauthorattachmentsid',
1557            'value' => $draftidattach,
1558        );
1559
1560        // Update the assessment.
1561        $result = mod_workshop_external::update_assessment($assessmentid, $data);
1562        $result = external_api::clean_returnvalue(mod_workshop_external::update_assessment_returns(), $result);
1563        $this->assertEquals(100, $result['rawgrade']);
1564        $this->assertTrue($result['status']);
1565
1566        // Get the assessment and check it was updated properly.
1567        $result = mod_workshop_external::get_assessment($assessmentid);
1568        $result = external_api::clean_returnvalue(mod_workshop_external::get_assessment_returns(), $result);
1569        $this->assertEquals(100, $result['assessment']['grade']);
1570        $this->assertEquals($this->student->id, $result['assessment']['reviewerid']);
1571        $this->assertEquals('Feedback for the author', $result['assessment']['feedbackauthor']);
1572        $this->assertCount(1, $result['assessment']['feedbackcontentfiles']);
1573        $this->assertCount(1, $result['assessment']['feedbackattachmentfiles']);
1574
1575        // Now, get again the form and check we received the data we already sent.
1576        $result = mod_workshop_external::get_assessment_form_definition($assessmentid);
1577        $result = external_api::clean_returnvalue(mod_workshop_external::get_assessment_form_definition_returns(), $result);
1578        foreach ($result['current'] as $currentdata) {
1579            if (strpos($currentdata['name'], 'peercomment__idx_') === 0) {
1580                $this->assertEquals('Some content', $currentdata['value']);
1581            } else if (strpos($currentdata['name'], 'grade__idx_') === 0) {
1582                $this->assertEquals(25, (int) $currentdata['value']);
1583            }
1584        }
1585    }
1586
1587    /**
1588     * Test get_grades.
1589     */
1590    public function test_get_grades() {
1591
1592        $timenow = time();
1593        $submissiongrade = array(
1594            'userid' => $this->student->id,
1595            'rawgrade' => 40,
1596            'feedback' => '',
1597            'feedbackformat' => 1,
1598            'datesubmitted' => $timenow,
1599            'dategraded' => $timenow,
1600        );
1601        $assessmentgrade = array(
1602            'userid' => $this->student->id,
1603            'rawgrade' => 10,
1604            'feedback' => '',
1605            'feedbackformat' => 1,
1606            'datesubmitted' => $timenow,
1607            'dategraded' => $timenow,
1608        );
1609
1610        workshop_grade_item_update($this->workshop, (object) $submissiongrade, (object) $assessmentgrade);
1611
1612        // First retrieve my grades.
1613        $this->setUser($this->student);
1614        $result = mod_workshop_external::get_grades($this->workshop->id);
1615        $result = external_api::clean_returnvalue(mod_workshop_external::get_grades_returns(), $result);
1616        $this->assertCount(0, $result['warnings']);
1617        $this->assertEquals($assessmentgrade['rawgrade'], $result['assessmentrawgrade']);
1618        $this->assertEquals($submissiongrade['rawgrade'], $result['submissionrawgrade']);
1619        $this->assertFalse($result['assessmentgradehidden']);
1620        $this->assertFalse($result['submissiongradehidden']);
1621        $this->assertEquals($assessmentgrade['rawgrade'] . ".00 / 20.00", $result['assessmentlongstrgrade']);
1622        $this->assertEquals($submissiongrade['rawgrade'] . ".00 / 80.00", $result['submissionlongstrgrade']);
1623
1624        // Second, teacher retrieve user grades.
1625        $this->setUser($this->teacher);
1626        $result = mod_workshop_external::get_grades($this->workshop->id, $this->student->id);
1627        $result = external_api::clean_returnvalue(mod_workshop_external::get_grades_returns(), $result);
1628        $this->assertCount(0, $result['warnings']);
1629        $this->assertEquals($assessmentgrade['rawgrade'], $result['assessmentrawgrade']);
1630        $this->assertEquals($submissiongrade['rawgrade'], $result['submissionrawgrade']);
1631        $this->assertFalse($result['assessmentgradehidden']);
1632        $this->assertFalse($result['submissiongradehidden']);
1633        $this->assertEquals($assessmentgrade['rawgrade'] . ".00 / 20.00", $result['assessmentlongstrgrade']);
1634        $this->assertEquals($submissiongrade['rawgrade'] . ".00 / 80.00", $result['submissionlongstrgrade']);
1635    }
1636
1637    /**
1638     * Test get_grades_other_student.
1639     */
1640    public function test_get_grades_other_student() {
1641
1642        // Create the submission that will be deleted.
1643        $submissionid = $this->create_test_submission($this->student);
1644
1645        $workshop = new workshop($this->workshop, $this->cm, $this->course);
1646        $workshop->switch_phase(workshop::PHASE_CLOSED);
1647        $this->setUser($this->anotherstudentg1);
1648        $this->expectException('moodle_exception');
1649        mod_workshop_external::get_grades($this->workshop->id, $this->student->id);
1650    }
1651
1652    /**
1653     * Test evaluate_assessment.
1654     */
1655    public function test_evaluate_assessment() {
1656        global $DB;
1657
1658        $workshopgenerator = $this->getDataGenerator()->get_plugin_generator('mod_workshop');
1659        $submissionid = $workshopgenerator->create_submission($this->workshop->id, $this->student->id);
1660        $assessmentid = $workshopgenerator->create_assessment($submissionid, $this->anotherstudentg1->id, array(
1661            'weight' => 3,
1662            'grade' => 20,
1663        ));
1664
1665        $this->setUser($this->teacher);
1666        $feedbacktext = 'The feedback';
1667        $feedbackformat = FORMAT_MOODLE;
1668        $weight = 10;
1669        $gradinggradeover = 10;
1670        $result = mod_workshop_external::evaluate_assessment($assessmentid, $feedbacktext, $feedbackformat, $weight,
1671            $gradinggradeover);
1672        $result = external_api::clean_returnvalue(mod_workshop_external::evaluate_assessment_returns(), $result);
1673        $this->assertTrue($result['status']);
1674
1675        $assessment = $DB->get_record('workshop_assessments', array('id' => $assessmentid));
1676        $this->assertEquals('The feedback', $assessment->feedbackreviewer);
1677        $this->assertEquals(10, $assessment->weight);
1678
1679        // Now test passing incorrect weight and grade values.
1680        $weight = 17;
1681        $gradinggradeover = 100;
1682        $result = mod_workshop_external::evaluate_assessment($assessmentid, $feedbacktext, $feedbackformat, $weight,
1683            $gradinggradeover);
1684        $result = external_api::clean_returnvalue(mod_workshop_external::evaluate_assessment_returns(), $result);
1685        $this->assertFalse($result['status']);
1686        $this->assertCount(2, $result['warnings']);
1687        $found = 0;
1688        foreach ($result['warnings'] as $warning) {
1689            if ($warning['item'] == 'weight' || $warning['item'] == 'gradinggradeover') {
1690                $found++;
1691            }
1692        }
1693        $this->assertEquals(2, $found);
1694    }
1695
1696    /**
1697     * Test evaluate_assessment_ignore_parameters.
1698     */
1699    public function test_evaluate_assessment_ignore_parameters() {
1700        $workshopgenerator = $this->getDataGenerator()->get_plugin_generator('mod_workshop');
1701        $submissionid = $workshopgenerator->create_submission($this->workshop->id, $this->student->id);
1702        $assessmentid = $workshopgenerator->create_assessment($submissionid, $this->anotherstudentg1->id, array(
1703            'weight' => 3,
1704            'grade' => 20,
1705        ));
1706
1707        assign_capability('mod/workshop:allocate', CAP_PROHIBIT, $this->teacherrole->id, $this->context->id);
1708        // Empty all the caches that may be affected  by this change.
1709        accesslib_clear_all_caches_for_unit_testing();
1710
1711        $this->setUser($this->teacher);
1712        $feedbacktext = 'The feedback';
1713        $feedbackformat = FORMAT_MOODLE;
1714        $weight = 10;
1715        $gradinggradeover = 19;
1716        $result = mod_workshop_external::evaluate_assessment($assessmentid, $feedbacktext, $feedbackformat, $weight,
1717            $gradinggradeover);
1718        $result = external_api::clean_returnvalue(mod_workshop_external::evaluate_assessment_returns(), $result);
1719        $this->assertTrue($result['status']);
1720
1721        $result = mod_workshop_external::get_assessment($assessmentid);
1722        $result = external_api::clean_returnvalue(mod_workshop_external::get_assessment_returns(), $result);
1723        $this->assertNotEquals(10, $result['assessment']['weight']);
1724    }
1725
1726    /**
1727     * Test evaluate_assessment_no_permissions.
1728     */
1729    public function test_evaluate_assessment_no_permissions() {
1730        $workshopgenerator = $this->getDataGenerator()->get_plugin_generator('mod_workshop');
1731        $submissionid = $workshopgenerator->create_submission($this->workshop->id, $this->student->id);
1732        $assessmentid = $workshopgenerator->create_assessment($submissionid, $this->anotherstudentg1->id, array(
1733            'weight' => 3,
1734            'grade' => 20,
1735        ));
1736
1737        $this->setUser($this->student);
1738        $feedbacktext = 'The feedback';
1739        $feedbackformat = FORMAT_MOODLE;
1740        $weight = 10;
1741        $gradinggradeover = 50;
1742        $this->expectException('moodle_exception');
1743        mod_workshop_external::evaluate_assessment($assessmentid, $feedbacktext, $feedbackformat, $weight, $gradinggradeover);
1744    }
1745
1746    /**
1747     * Test get_grades_report.
1748     */
1749    public function test_get_grades_report() {
1750
1751        $workshop = new workshop($this->workshop, $this->cm, $this->course);
1752        $workshopgenerator = $this->getDataGenerator()->get_plugin_generator('mod_workshop');
1753        $submissionid1 = $workshopgenerator->create_submission($this->workshop->id, $this->student->id);
1754        $submissionid2 = $workshopgenerator->create_submission($this->workshop->id, $this->anotherstudentg1->id);
1755
1756        $assessmentid1 = $workshopgenerator->create_assessment($submissionid2, $this->student->id, array(
1757            'weight' => 100,
1758            'grade' => 50,
1759        ));
1760        $assessmentid2 = $workshopgenerator->create_assessment($submissionid1, $this->anotherstudentg1->id, array(
1761            'weight' => 100,
1762            'grade' => 55,
1763        ));
1764
1765        $workshop->switch_phase(workshop::PHASE_CLOSED);
1766        $this->setUser($this->teacher);
1767        $result = mod_workshop_external::get_grades_report($this->workshop->id);
1768        $result = external_api::clean_returnvalue(mod_workshop_external::get_grades_report_returns(), $result);
1769        $this->assertEquals(3, $result['report']['totalcount']); // Expect 3 potential submissions.
1770
1771        foreach ($result['report']['grades'] as $grade) {
1772            if ($grade['userid'] == $this->student->id) {
1773                $this->assertEquals($this->anotherstudentg1->id, $grade['reviewedby'][0]['userid']); // Check reviewer.
1774                $this->assertEquals($this->anotherstudentg1->id, $grade['reviewerof'][0]['userid']); // Check reviewer.
1775                $this->assertEquals($workshop->real_grade(50), $grade['reviewerof'][0]['grade']); // Check grade (converted).
1776                $this->assertEquals($workshop->real_grade(55), $grade['reviewedby'][0]['grade']); // Check grade (converted).
1777            } else if ($grade['userid'] == $this->anotherstudentg1->id) {
1778                $this->assertEquals($this->student->id, $grade['reviewedby'][0]['userid']); // Check reviewer.
1779                $this->assertEquals($this->student->id, $grade['reviewerof'][0]['userid']); // Check reviewer.
1780                $this->assertEquals($workshop->real_grade(55), $grade['reviewerof'][0]['grade']); // Check grade (converted).
1781                $this->assertEquals($workshop->real_grade(50), $grade['reviewedby'][0]['grade']); // Check grade (converted).
1782            }
1783        }
1784        // Now check pagination.
1785        $result = mod_workshop_external::get_grades_report($this->workshop->id, 0, 'lastname', 'ASC', 0, 1);
1786        $result = external_api::clean_returnvalue(mod_workshop_external::get_grades_report_returns(), $result);
1787        $this->assertEquals(3, $result['report']['totalcount']); // Expect the total count.
1788        $this->assertCount(1, $result['report']['grades']);
1789
1790        // Groups filtering.
1791        $result = mod_workshop_external::get_grades_report($this->workshop->id, $this->group1->id);
1792        $result = external_api::clean_returnvalue(mod_workshop_external::get_grades_report_returns(), $result);
1793        $this->assertEquals(2, $result['report']['totalcount']); // Expect the group count.
1794    }
1795
1796    /**
1797     * Test get_grades_report_invalid_phase.
1798     */
1799    public function test_get_grades_report_invalid_phase() {
1800        $this->setUser($this->teacher);
1801        $this->expectException('moodle_exception');
1802        $this->expectExceptionMessage(get_string('nothingfound', 'workshop'));
1803        mod_workshop_external::get_grades_report($this->workshop->id);
1804    }
1805
1806    /**
1807     * Test get_grades_report_missing_permissions.
1808     */
1809    public function test_get_grades_report_missing_permissions() {
1810        $this->setUser($this->student);
1811        $this->expectException('required_capability_exception');
1812        mod_workshop_external::get_grades_report($this->workshop->id);
1813    }
1814
1815    /**
1816     * Test test_view_submission.
1817     */
1818    public function test_view_submission() {
1819
1820        // Create a couple of submissions with files.
1821        $firstsubmissionid = $this->create_test_submission($this->student);  // Create submission with files.
1822
1823        // Trigger and capture the event.
1824        $sink = $this->redirectEvents();
1825
1826        $this->setUser($this->student);
1827        $result = mod_workshop_external::view_submission($firstsubmissionid);
1828        $result = external_api::clean_returnvalue(mod_workshop_external::view_submission_returns(), $result);
1829
1830        $events = $sink->get_events();
1831        $this->assertCount(1, $events);
1832        $event = array_shift($events);
1833
1834        // Checking that the event contains the expected values.
1835        $this->assertInstanceOf('\mod_workshop\event\submission_viewed', $event);
1836        $this->assertEquals($this->context, $event->get_context());
1837        $moodleworkshop = new \moodle_url('/mod/workshop/submission.php', array('id' => $firstsubmissionid,
1838            'cmid' => $this->cm->id));
1839        $this->assertEquals($moodleworkshop, $event->get_url());
1840        $this->assertEventContextNotUsed($event);
1841        $this->assertNotEmpty($event->get_name());
1842
1843    }
1844
1845    /**
1846     * Test evaluate_submission.
1847     */
1848    public function test_evaluate_submission() {
1849        global $DB;
1850
1851        $workshopgenerator = $this->getDataGenerator()->get_plugin_generator('mod_workshop');
1852        $submissionid = $workshopgenerator->create_submission($this->workshop->id, $this->student->id);
1853
1854        $workshop = new workshop($this->workshop, $this->cm, $this->course);
1855        $workshop->switch_phase(workshop::PHASE_EVALUATION);
1856
1857        $this->setUser($this->teacher);
1858        $feedbacktext = 'The feedback';
1859        $feedbackformat = FORMAT_MOODLE;
1860        $published = 1;
1861        $gradeover = 10;
1862        $result = mod_workshop_external::evaluate_submission($submissionid, $feedbacktext, $feedbackformat, $published,
1863            $gradeover);
1864        $result = external_api::clean_returnvalue(mod_workshop_external::evaluate_submission_returns(), $result);
1865        $this->assertTrue($result['status']);
1866
1867        $submission = $DB->get_record('workshop_submissions', array('id' => $submissionid));
1868        $this->assertEquals($feedbacktext, $submission->feedbackauthor);
1869        $this->assertEquals($workshop->raw_grade_value($gradeover, $workshop->grade), $submission->gradeover);  // Expected grade.
1870        $this->assertEquals(1, $submission->published); // Submission published.
1871    }
1872
1873    /**
1874     * Test evaluate_submission_invalid_phase_for_override.
1875     */
1876    public function test_evaluate_submission_invalid_phase_for_override() {
1877        global $DB;
1878
1879        $workshopgenerator = $this->getDataGenerator()->get_plugin_generator('mod_workshop');
1880        $submissionid = $workshopgenerator->create_submission($this->workshop->id, $this->student->id);
1881
1882        $this->setUser($this->teacher);
1883        $feedbacktext = 'The feedback';
1884        $feedbackformat = FORMAT_MOODLE;
1885        $published = 1;
1886        $gradeover = 10;
1887        $result = mod_workshop_external::evaluate_submission($submissionid, $feedbacktext, $feedbackformat, $published,
1888            $gradeover);
1889        $result = external_api::clean_returnvalue(mod_workshop_external::evaluate_submission_returns(), $result);
1890        $this->assertTrue($result['status']);
1891
1892        $submission = $DB->get_record('workshop_submissions', array('id' => $submissionid));
1893        $this->assertEquals('', $submission->feedbackauthor);   // Feedback and grade not updated.
1894        $this->assertEquals(0, $submission->gradeover);
1895        $this->assertEquals(1, $submission->published); // Publishing status correctly updated.
1896    }
1897
1898    /**
1899     * Test evaluate_submission_no_permissions.
1900     */
1901    public function test_evaluate_submission_no_permissions() {
1902
1903        $workshopgenerator = $this->getDataGenerator()->get_plugin_generator('mod_workshop');
1904        $submissionid = $workshopgenerator->create_submission($this->workshop->id, $this->student->id);
1905        $workshop = new workshop($this->workshop, $this->cm, $this->course);
1906        $workshop->switch_phase(workshop::PHASE_EVALUATION);
1907
1908        $this->setUser($this->student);
1909        $feedbacktext = 'The feedback';
1910        $feedbackformat = FORMAT_MOODLE;
1911        $published = 1;
1912        $gradeover = 50;
1913        $this->expectException('moodle_exception');
1914        mod_workshop_external::evaluate_submission($submissionid, $feedbacktext, $feedbackformat, $published, $gradeover);
1915    }
1916
1917    /**
1918     * Test evaluate_submission_invalid_grade.
1919     */
1920    public function test_evaluate_submission_invalid_grade() {
1921
1922        $workshopgenerator = $this->getDataGenerator()->get_plugin_generator('mod_workshop');
1923        $submissionid = $workshopgenerator->create_submission($this->workshop->id, $this->student->id);
1924        $workshop = new workshop($this->workshop, $this->cm, $this->course);
1925        $workshop->switch_phase(workshop::PHASE_EVALUATION);
1926
1927        $this->setUser($this->teacher);
1928        $feedbacktext = 'The feedback';
1929        $feedbackformat = FORMAT_MOODLE;
1930        $published = 1;
1931        $gradeover = 150;
1932        $result = mod_workshop_external::evaluate_submission($submissionid, $feedbacktext, $feedbackformat, $published, $gradeover);
1933        $result = external_api::clean_returnvalue(mod_workshop_external::evaluate_submission_returns(), $result);
1934        $this->assertCount(1, $result['warnings']);
1935        $this->assertFalse($result['status']);
1936        $this->assertEquals('gradeover', $result['warnings'][0]['item']);
1937    }
1938}
1939