MDL-42995 mod_assign: Extended tests for update_calendar method
[moodle.git] / mod / assign / tests / locallib_test.php
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/>.
17 /**
18  * Unit tests for (some of) mod/assign/locallib.php.
19  *
20  * @package    mod_assign
21  * @category   phpunit
22  * @copyright  1999 onwards Martin Dougiamas  {@link http://moodle.com}
23  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24  */
27 defined('MOODLE_INTERNAL') || die();
29 global $CFG;
30 require_once($CFG->dirroot . '/mod/assign/locallib.php');
31 require_once($CFG->dirroot . '/mod/assign/upgradelib.php');
32 require_once($CFG->dirroot . '/mod/assign/tests/base_test.php');
34 /**
35  * Unit tests for (some of) mod/assign/locallib.php.
36  *
37  * @copyright  1999 onwards Martin Dougiamas  {@link http://moodle.com}
38  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
39  */
40 class mod_assign_locallib_testcase extends mod_assign_base_testcase {
42     public function test_return_links() {
43         global $PAGE;
44         $this->setUser($this->editingteachers[0]);
45         $returnaction = 'RETURNACTION';
46         $returnparams = array('param'=>'1');
47         $assign = $this->create_instance();
48         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
49         $assign->register_return_link($returnaction, $returnparams);
50         $this->assertEquals($returnaction, $assign->get_return_action());
51         $this->assertEquals($returnparams, $assign->get_return_params());
52     }
54     public function test_get_feedback_plugins() {
55         $this->setUser($this->editingteachers[0]);
56         $assign = $this->create_instance();
57         $installedplugins = array_keys(core_component::get_plugin_list('assignfeedback'));
59         foreach ($assign->get_feedback_plugins() as $plugin) {
60             $this->assertContains($plugin->get_type(), $installedplugins, 'Feedback plugin not in list of installed plugins');
61         }
62     }
64     public function test_get_submission_plugins() {
65         $this->setUser($this->editingteachers[0]);
66         $assign = $this->create_instance();
67         $installedplugins = array_keys(core_component::get_plugin_list('assignsubmission'));
69         foreach ($assign->get_submission_plugins() as $plugin) {
70             $this->assertContains($plugin->get_type(), $installedplugins, 'Submission plugin not in list of installed plugins');
71         }
72     }
74     public function test_is_blind_marking() {
75         $this->setUser($this->editingteachers[0]);
76         $assign = $this->create_instance(array('blindmarking'=>1));
77         $this->assertEquals(true, $assign->is_blind_marking());
79         // Test cannot see student names.
80         $gradingtable = new assign_grading_table($assign, 1, '', 0, true);
81         $output = $assign->get_renderer()->render($gradingtable);
82         $this->assertEquals(true, strpos($output, get_string('hiddenuser', 'assign')));
84         // Test students cannot reveal identities.
85         $nopermission = false;
86         $this->students[0]->ignoresesskey = true;
87         $this->setUser($this->students[0]);
88         $this->setExpectedException('required_capability_exception');
89         $assign->reveal_identities();
90         $this->students[0]->ignoresesskey = false;
92         // Test teachers cannot reveal identities.
93         $nopermission = false;
94         $this->teachers[0]->ignoresesskey = true;
95         $this->setUser($this->teachers[0]);
96         $this->setExpectedException('required_capability_exception');
97         $assign->reveal_identities();
98         $this->teachers[0]->ignoresesskey = false;
100         // Test sesskey is required.
101         $this->setUser($this->editingteachers[0]);
102         $this->setExpectedException('moodle_exception');
103         $assign->reveal_identities();
105         // Test editingteacher can reveal identities if sesskey is ignored.
106         $this->editingteachers[0]->ignoresesskey = true;
107         $this->setUser($this->editingteachers[0]);
108         $assign->reveal_identities();
109         $this->assertEquals(false, $assign->is_blind_marking());
110         $this->editingteachers[0]->ignoresesskey = false;
112         // Test student names are visible.
113         $gradingtable = new assign_grading_table($assign, 1, '', 0, true);
114         $output = $assign->get_renderer()->render($gradingtable);
115         $this->assertEquals(false, strpos($output, get_string('hiddenuser', 'assign')));
117         // Set this back to default.
118         $this->editingteachers[0]->ignoresesskey = false;
119     }
121     public function test_show_intro() {
122         // Test whether we are showing the intro at the correct times.
123         $this->setUser($this->editingteachers[0]);
124         $assign = $this->create_instance(array('alwaysshowdescription'=>1));
126         $this->assertEquals(true, $assign->testable_show_intro());
128         $tomorrow = time() + (24*60*60);
130         $assign = $this->create_instance(array('alwaysshowdescription'=>0,
131                                                'allowsubmissionsfromdate'=>$tomorrow));
132         $this->assertEquals(false, $assign->testable_show_intro());
133         $yesterday = time() - (24*60*60);
134         $assign = $this->create_instance(array('alwaysshowdescription'=>0,
135                                                'allowsubmissionsfromdate'=>$yesterday));
136         $this->assertEquals(true, $assign->testable_show_intro());
137     }
139     public function test_has_submissions_or_grades() {
140         $this->setUser($this->editingteachers[0]);
141         $assign = $this->create_instance(array('assignsubmission_onlinetext_enabled'=>1));
143         $instance = $assign->get_instance();
145         // Should start empty.
146         $this->assertEquals(false, $assign->has_submissions_or_grades());
148         // Simulate a submission.
149         $this->setUser($this->students[0]);
150         $submission = $assign->get_user_submission($this->students[0]->id, true);
151         $data = new stdClass();
152         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
153                                          'text'=>'Submission text',
154                                          'format'=>FORMAT_MOODLE);
155         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
156         $plugin->save($submission, $data);
158         // Now test again.
159         $this->assertEquals(true, $assign->has_submissions_or_grades());
160         // Set this back to default.
161         $this->students[0]->ignoresesskey = false;
162     }
164     public function test_delete_grades() {
165         $this->setUser($this->editingteachers[0]);
166         $assign = $this->create_instance();
168         // Simulate adding a grade.
169         $this->setUser($this->teachers[0]);
170         $data = new stdClass();
171         $data->grade = '50.0';
172         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
174         // Now see if the data is in the gradebook.
175         $gradinginfo = grade_get_grades($this->course->id,
176                                         'mod',
177                                         'assign',
178                                         $assign->get_instance()->id);
180         $this->assertNotEquals(0, count($gradinginfo->items));
182         $assign->testable_delete_grades();
183         $gradinginfo = grade_get_grades($this->course->id,
184                                         'mod',
185                                         'assign',
186                                         $assign->get_instance()->id);
188         $this->assertEquals(0, count($gradinginfo->items));
189     }
191     public function test_delete_instance() {
192         $this->setUser($this->editingteachers[0]);
193         $assign = $this->create_instance(array('assignsubmission_onlinetext_enabled'=>1));
195         // Simulate adding a grade.
196         $this->setUser($this->teachers[0]);
197         $data = new stdClass();
198         $data->grade = '50.0';
199         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
201         // Simulate a submission.
202         $this->setUser($this->students[0]);
203         $submission = $assign->get_user_submission($this->students[0]->id, true);
204         $data = new stdClass();
205         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
206                                          'text'=>'Submission text',
207                                          'format'=>FORMAT_MOODLE);
208         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
209         $plugin->save($submission, $data);
211         // Now try and delete.
212         $this->assertEquals(true, $assign->delete_instance());
213     }
215     public function test_reset_userdata() {
216         global $DB;
218         $now = time();
219         $this->setUser($this->editingteachers[0]);
220         $assign = $this->create_instance(array('assignsubmission_onlinetext_enabled'=>1,
221                                                'duedate'=>$now));
223         // Simulate adding a grade.
224         $this->setUser($this->teachers[0]);
225         $data = new stdClass();
226         $data->grade = '50.0';
227         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
229         // Simulate a submission.
230         $this->setUser($this->students[0]);
231         $submission = $assign->get_user_submission($this->students[0]->id, true);
232         $data = new stdClass();
233         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
234                                          'text'=>'Submission text',
235                                          'format'=>FORMAT_MOODLE);
236         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
237         $plugin->save($submission, $data);
239         $this->assertEquals(true, $assign->has_submissions_or_grades());
240         // Now try and reset.
241         $data = new stdClass();
242         $data->reset_assign_submissions = 1;
243         $data->reset_gradebook_grades = 1;
244         $data->courseid = $this->course->id;
245         $data->timeshift = 24*60*60;
246         $this->setUser($this->editingteachers[0]);
247         $assign->reset_userdata($data);
248         $this->assertEquals(false, $assign->has_submissions_or_grades());
250         // Reload the instance data.
251         $instance = $DB->get_record('assign', array('id'=>$assign->get_instance()->id));
252         $this->assertEquals($now + 24*60*60, $instance->duedate);
254         // Test reset using assign_reset_userdata().
255         $assignduedate = $instance->duedate; // Keep old updated value for comparison.
256         $data->timeshift = 2*24*60*60;
257         assign_reset_userdata($data);
258         $instance = $DB->get_record('assign', array('id' => $assign->get_instance()->id));
259         $this->assertEquals($assignduedate + 2*24*60*60, $instance->duedate);
261         // Create one more assignment and reset, make sure time shifted for previous assignment is not changed.
262         $assign2 = $this->create_instance(array('assignsubmission_onlinetext_enabled' => 1,
263                                                'duedate' => $now));
264         $assignduedate = $instance->duedate;
265         $data->timeshift = 3*24*60*60;
266         $assign2->reset_userdata($data);
267         $instance = $DB->get_record('assign', array('id' => $assign->get_instance()->id));
268         $this->assertEquals($assignduedate, $instance->duedate);
269         $instance2 = $DB->get_record('assign', array('id' => $assign2->get_instance()->id));
270         $this->assertEquals($now + 3*24*60*60, $instance2->duedate);
272         // Reset both assignments using assign_reset_userdata() and make sure both assignments have same date.
273         $assignduedate = $instance->duedate;
274         $assign2duedate = $instance2->duedate;
275         $data->timeshift = 4*24*60*60;
276         assign_reset_userdata($data);
277         $instance = $DB->get_record('assign', array('id' => $assign->get_instance()->id));
278         $this->assertEquals($assignduedate + 4*24*60*60, $instance->duedate);
279         $instance2 = $DB->get_record('assign', array('id' => $assign2->get_instance()->id));
280         $this->assertEquals($assign2duedate + 4*24*60*60, $instance2->duedate);
281     }
283     public function test_plugin_settings() {
284         global $DB;
286         $now = time();
287         $this->setUser($this->editingteachers[0]);
288         $assign = $this->create_instance(array('assignsubmission_file_enabled'=>1,
289                                                'assignsubmission_file_maxfiles'=>12,
290                                                'assignsubmission_file_maxsizebytes'=>10));
292         $plugin = $assign->get_submission_plugin_by_type('file');
293         $this->assertEquals('12', $plugin->get_config('maxfilesubmissions'));
294     }
296     public function test_update_calendar() {
297         global $DB;
299         $this->setUser($this->editingteachers[0]);
300         $userctx = context_user::instance($this->editingteachers[0]->id)->id;
302         // Hack to pretend that there was an editor involved. We need both $_POST and $_REQUEST, and a sesskey.
303         $draftid = file_get_unused_draft_itemid();
304         $_REQUEST['introeditor'] = $draftid;
305         $_POST['introeditor'] = $draftid;
306         $_POST['sesskey'] = sesskey();
308         // Write links to a draft area.
309         $fakearealink1 = file_rewrite_pluginfile_urls('<a href="@@PLUGINFILE@@/pic.gif">link</a>', 'draftfile.php', $userctx,
310             'user', 'draft', $draftid);
311         $fakearealink2 = file_rewrite_pluginfile_urls('<a href="@@PLUGINFILE@@/pic.gif">new</a>', 'draftfile.php', $userctx,
312             'user', 'draft', $draftid);
314         // Create a new assignment with links to a draft area.
315         $now = time();
316         $assign = $this->create_instance(array(
317             'duedate' => $now,
318             'intro' => $fakearealink1,
319             'introformat' => FORMAT_HTML
320         ));
322         // See if there is an event in the calendar.
323         $params = array('modulename'=>'assign', 'instance'=>$assign->get_instance()->id);
324         $event = $DB->get_record('event', $params);
325         $this->assertNotEmpty($event);
326         $this->assertSame('link', $event->description);     // The pluginfile links are removed.
328         // Make sure the same works when updating the assignment.
329         $instance = $assign->get_instance();
330         $instance->instance = $instance->id;
331         $instance->intro = $fakearealink2;
332         $instance->introformat = FORMAT_HTML;
333         $assign->update_instance($instance);
334         $params = array('modulename' => 'assign', 'instance' => $assign->get_instance()->id);
335         $event = $DB->get_record('event', $params);
336         $this->assertNotEmpty($event);
337         $this->assertSame('new', $event->description);     // The pluginfile links are removed.
338     }
340     public function test_update_instance() {
341         global $DB;
343         $this->setUser($this->editingteachers[0]);
344         $assign = $this->create_instance(array('assignsubmission_onlinetext_enabled'=>1));
346         $now = time();
347         $instance = $assign->get_instance();
348         $instance->duedate = $now;
349         $instance->instance = $instance->id;
350         $instance->assignsubmission_onlinetext_enabled = 1;
352         $assign->update_instance($instance);
354         $instance = $DB->get_record('assign', array('id'=>$assign->get_instance()->id));
355         $this->assertEquals($now, $instance->duedate);
356     }
358     public function test_cannot_submit_empty() {
359         global $PAGE;
361         $this->setUser($this->editingteachers[0]);
362         $assign = $this->create_instance(array('submissiondrafts'=>1));
364         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
366         // Test you cannot see the submit button for an offline assignment regardless.
367         $this->setUser($this->students[0]);
368         $output = $assign->view_student_summary($this->students[0], true);
369         $this->assertNotContains(get_string('submitassignment', 'assign'), $output, 'Can submit empty offline assignment');
371         // Test you cannot see the submit button for an online text assignment with no submission.
372         $this->setUser($this->editingteachers[0]);
373         $instance = $assign->get_instance();
374         $instance->instance = $instance->id;
375         $instance->assignsubmission_onlinetext_enabled = 1;
377         $assign->update_instance($instance);
378         $this->setUser($this->students[0]);
379         $output = $assign->view_student_summary($this->students[0], true);
380         $this->assertNotContains(get_string('submitassignment', 'assign'), $output, 'Cannot submit empty onlinetext assignment');
382         // Simulate a submission.
383         $submission = $assign->get_user_submission($this->students[0]->id, true);
384         $data = new stdClass();
385         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
386                                          'text'=>'Submission text',
387                                          'format'=>FORMAT_MOODLE);
388         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
389         $plugin->save($submission, $data);
390         // Test you can see the submit button for an online text assignment with a submission.
391         $output = $assign->view_student_summary($this->students[0], true);
392         $this->assertContains(get_string('submitassignment', 'assign'), $output, 'Can submit non empty onlinetext assignment');
393     }
395     public function test_list_participants() {
396         $this->create_extra_users();
397         $this->setUser($this->editingteachers[0]);
398         $assign = $this->create_instance(array('grade'=>100));
400         $this->assertEquals(self::DEFAULT_STUDENT_COUNT + self::EXTRA_STUDENT_COUNT, count($assign->list_participants(null, true)));
402         // Teacher with user preference set should see suspended users as well.
403         set_user_preference('grade_report_showonlyactiveenrol', false);
404         $assign = $this->create_instance(array('grade'=>100));
405         $this->assertEquals(self::DEFAULT_STUDENT_COUNT + self::EXTRA_STUDENT_COUNT + self::EXTRA_SUSPENDED_COUNT,
406                 count($assign->list_participants(null, true)));
408         // Non-editing teacher should not see suspended users, even if user preference is set.
409         $this->setUser($this->teachers[0]);
410         set_user_preference('grade_report_showonlyactiveenrol', false);
411         $assign = $this->create_instance(array('grade'=>100));
412         $this->assertEquals(self::DEFAULT_STUDENT_COUNT + self::EXTRA_STUDENT_COUNT, count($assign->list_participants(null, true)));
413     }
415     public function test_count_teams() {
416         $this->create_extra_users();
417         $this->setUser($this->editingteachers[0]);
418         $assign = $this->create_instance(array('teamsubmission'=>1));
420         $this->assertEquals(self::GROUP_COUNT + 1, $assign->count_teams());
421     }
423     public function test_count_submissions() {
424         $this->create_extra_users();
425         $this->setUser($this->editingteachers[0]);
426         $assign = $this->create_instance(array('assignsubmission_onlinetext_enabled'=>1));
428         // Simulate a submission.
429         $this->setUser($this->extrastudents[0]);
430         $submission = $assign->get_user_submission($this->extrastudents[0]->id, true);
431         // Leave this one as DRAFT.
432         $data = new stdClass();
433         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
434                                          'text'=>'Submission text',
435                                          'format'=>FORMAT_MOODLE);
436         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
437         $plugin->save($submission, $data);
439         // Simulate adding a grade.
440         $this->setUser($this->teachers[0]);
441         $data = new stdClass();
442         $data->grade = '50.0';
443         $assign->testable_apply_grade_to_user($data, $this->extrastudents[0]->id, 0);
445         // Simulate a submission.
446         $this->setUser($this->extrastudents[1]);
447         $submission = $assign->get_user_submission($this->extrastudents[1]->id, true);
448         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
449         $assign->testable_update_submission($submission, $this->extrastudents[1]->id, true, false);
450         $data = new stdClass();
451         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
452                                          'text'=>'Submission text',
453                                          'format'=>FORMAT_MOODLE);
454         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
455         $plugin->save($submission, $data);
457         // Simulate a submission.
458         $this->setUser($this->extrastudents[2]);
459         $submission = $assign->get_user_submission($this->extrastudents[2]->id, true);
460         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
461         $assign->testable_update_submission($submission, $this->extrastudents[2]->id, true, false);
462         $data = new stdClass();
463         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
464                                          'text'=>'Submission text',
465                                          'format'=>FORMAT_MOODLE);
466         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
467         $plugin->save($submission, $data);
469         // Simulate a submission.
470         $this->setUser($this->extrastudents[3]);
471         $submission = $assign->get_user_submission($this->extrastudents[3]->id, true);
472         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
473         $assign->testable_update_submission($submission, $this->extrastudents[3]->id, true, false);
474         $data = new stdClass();
475         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
476                                          'text'=>'Submission text',
477                                          'format'=>FORMAT_MOODLE);
478         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
479         $plugin->save($submission, $data);
481         // Simulate a submission for suspended user, this will never be counted.
482         $this->setUser($this->extrastudents[3]);
483         $submission = $assign->get_user_submission($this->extrasuspendedstudents[0]->id, true);
484         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
485         $assign->testable_update_submission($submission, $this->extrasuspendedstudents[0]->id, true, false);
486         $data = new stdClass();
487         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
488                                          'text'=>'Submission text',
489                                          'format'=>FORMAT_MOODLE);
490         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
491         $plugin->save($submission, $data);
493         // Simulate adding a grade.
494         $this->setUser($this->teachers[0]);
495         $data = new stdClass();
496         $data->grade = '50.0';
497         $assign->testable_apply_grade_to_user($data, $this->extrastudents[3]->id, 0);
498         $assign->testable_apply_grade_to_user($data, $this->extrasuspendedstudents[0]->id, 0);
500         $this->assertEquals(2, $assign->count_grades());
501         $this->assertEquals(4, $assign->count_submissions());
502         $this->assertEquals(2, $assign->count_submissions_need_grading());
503         $this->assertEquals(3, $assign->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_SUBMITTED));
504         $this->assertEquals(1, $assign->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_DRAFT));
505     }
507     public function test_get_grading_userid_list() {
508         $this->create_extra_users();
509         $this->setUser($this->editingteachers[0]);
510         $assign = $this->create_instance();
512         $users = $assign->testable_get_grading_userid_list();
513         $this->assertEquals(self::DEFAULT_STUDENT_COUNT + self::EXTRA_STUDENT_COUNT, count($users));
515         $this->setUser($this->editingteachers[0]);
516         set_user_preference('grade_report_showonlyactiveenrol', false);
517         $assign = $this->create_instance();
519         $users = $assign->testable_get_grading_userid_list();
520         $this->assertEquals(self::DEFAULT_STUDENT_COUNT + self::EXTRA_STUDENT_COUNT + self::EXTRA_SUSPENDED_COUNT, count($users));
521     }
523     public function test_cron() {
524         // First run cron so there are no messages waiting to be sent (from other tests).
525         cron_setup_user();
526         assign::cron();
528         // Now create an assignment and add some feedback.
529         $this->setUser($this->editingteachers[0]);
530         $assign = $this->create_instance();
532         // Simulate adding a grade.
533         $this->setUser($this->teachers[0]);
534         $data = new stdClass();
535         $data->grade = '50.0';
536         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
537         $assign->testable_apply_grade_to_user($data, $this->students[1]->id, 0);
539         // Now run cron and see that one message was sent.
540         $this->preventResetByRollback();
541         $sink = $this->redirectMessages();
542         cron_setup_user();
543         $this->expectOutputRegex('/Done processing 2 assignment submissions/');
544         assign::cron();
546         $messages = $sink->get_messages();
547         $this->assertEquals(2, count($messages));
548         $this->assertEquals(1, $messages[0]->notification);
549         $this->assertEquals($assign->get_instance()->name, $messages[0]->contexturlname);
550     }
552     public function test_is_graded() {
553         $this->setUser($this->editingteachers[0]);
554         $assign = $this->create_instance();
556         // Simulate adding a grade.
557         $this->setUser($this->teachers[0]);
558         $data = new stdClass();
559         $data->grade = '50.0';
560         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
562         $this->assertEquals(true, $assign->testable_is_graded($this->students[0]->id));
563         $this->assertEquals(false, $assign->testable_is_graded($this->students[1]->id));
564     }
566     public function test_can_view_submission() {
567         $this->create_extra_users();
568         $this->setUser($this->editingteachers[0]);
569         $assign = $this->create_instance();
571         $this->setUser($this->students[0]);
572         $this->assertEquals(true, $assign->can_view_submission($this->students[0]->id));
573         $this->assertEquals(false, $assign->can_view_submission($this->students[1]->id));
574         $this->assertEquals(false, $assign->can_view_submission($this->teachers[0]->id));
575         $this->setUser($this->teachers[0]);
576         $this->assertEquals(true, $assign->can_view_submission($this->students[0]->id));
577         $this->assertEquals(true, $assign->can_view_submission($this->students[1]->id));
578         $this->assertEquals(true, $assign->can_view_submission($this->teachers[0]->id));
579         $this->assertEquals(false, $assign->can_view_submission($this->extrasuspendedstudents[0]->id));
580         $this->setUser($this->editingteachers[0]);
581         $this->assertEquals(true, $assign->can_view_submission($this->students[0]->id));
582         $this->assertEquals(true, $assign->can_view_submission($this->students[1]->id));
583         $this->assertEquals(true, $assign->can_view_submission($this->teachers[0]->id));
584         $this->assertEquals(true, $assign->can_view_submission($this->extrasuspendedstudents[0]->id));
585     }
588     public function test_update_submission() {
589         $this->create_extra_users();
590         $this->setUser($this->editingteachers[0]);
591         $assign = $this->create_instance();
593         $this->setUser($this->extrastudents[0]);
594         $now = time();
595         $submission = $assign->get_user_submission($this->extrastudents[0]->id, true);
596         $assign->testable_update_submission($submission, $this->extrastudents[0]->id, true, false);
598         $this->setUser($this->teachers[0]);
599         // Verify the gradebook update.
600         $gradinginfo = grade_get_grades($this->course->id,
601                                         'mod',
602                                         'assign',
603                                         $assign->get_instance()->id,
604                                         $this->extrastudents[0]->id);
606         $this->assertEquals($this->extrastudents[0]->id,
607                             $gradinginfo->items[0]->grades[$this->extrastudents[0]->id]->usermodified);
609         // Now verify group assignments.
610         $this->setUser($this->editingteachers[0]);
611         $assign = $this->create_instance(array('teamsubmission'=>1));
613         $this->setUser($this->extrastudents[0]);
614         $now = time();
615         $submission = $assign->get_group_submission($this->extrastudents[0]->id, 0, true);
616         $assign->testable_update_submission($submission, $this->extrastudents[0]->id, true, true);
618         // Check that at least 2 active members and 1 suspended member of the submission group had their submission updated.
620         $this->setUser($this->editingteachers[0]);
621         $gradinginfo = grade_get_grades($this->course->id,
622                                         'mod',
623                                         'assign',
624                                         $assign->get_instance()->id,
625                                         $this->extrastudents[0]->id);
627         $this->assertEquals($this->extrastudents[0]->id,
628                             $gradinginfo->items[0]->grades[$this->extrastudents[0]->id]->usermodified);
630         $gradinginfo = grade_get_grades($this->course->id,
631                                         'mod',
632                                         'assign',
633                                         $assign->get_instance()->id,
634                                         $this->extrastudents[self::GROUP_COUNT]->id);
636         $this->assertEquals($this->extrastudents[self::GROUP_COUNT]->id,
637                             $gradinginfo->items[0]->grades[$this->extrastudents[self::GROUP_COUNT]->id]->usermodified);
639         $gradinginfo = grade_get_grades($this->course->id,
640                                         'mod',
641                                         'assign',
642                                         $assign->get_instance()->id,
643                                         $this->extrasuspendedstudents[0]->id);
644         $this->assertEquals($this->extrasuspendedstudents[0]->id,
645                             $gradinginfo->items[0]->grades[$this->extrasuspendedstudents[0]->id]->usermodified);
647         // Check the same with non-editing teacher and make sure submission is not updated for suspended user.
648         $this->setUser($this->editingteachers[0]);
649         $assign = $this->create_instance(array('teamsubmission'=>1));
651         $this->setUser($this->extrastudents[1]);
652         $now = time();
653         $submission = $assign->get_group_submission($this->extrastudents[1]->id, 0, true);
654         $assign->testable_update_submission($submission, $this->extrastudents[1]->id, true, true);
656         $this->setUser($this->teachers[0]);
657         $gradinginfo = grade_get_grades($this->course->id,
658                                         'mod',
659                                         'assign',
660                                         $assign->get_instance()->id,
661                                         $this->extrastudents[1]->id);
663         $this->assertEquals($this->extrastudents[1]->id,
664                             $gradinginfo->items[0]->grades[$this->extrastudents[1]->id]->usermodified);
666         $gradinginfo = grade_get_grades($this->course->id,
667                                         'mod',
668                                         'assign',
669                                         $assign->get_instance()->id,
670                                         $this->extrastudents[self::GROUP_COUNT+1]->id);
672         $this->assertEquals($this->extrastudents[self::GROUP_COUNT+1]->id,
673                             $gradinginfo->items[0]->grades[$this->extrastudents[self::GROUP_COUNT+1]->id]->usermodified);
675         $gradinginfo = grade_get_grades($this->course->id,
676                                         'mod',
677                                         'assign',
678                                         $assign->get_instance()->id,
679                                         $this->extrasuspendedstudents[1]->id);
680         $this->assertEquals($this->extrasuspendedstudents[1]->id,
681                             $gradinginfo->items[0]->grades[$this->extrasuspendedstudents[1]->id]->usermodified);
683         // Now verify blind marking.
684         $this->setUser($this->editingteachers[0]);
685         $assign = $this->create_instance(array('blindmarking'=>1));
687         $this->setUser($this->extrastudents[0]);
688         $now = time();
689         $submission = $assign->get_user_submission($this->extrastudents[0]->id, true);
690         $assign->testable_update_submission($submission, $this->extrastudents[0]->id, true, false);
692         $this->setUser($this->editingteachers[0]);
693         $gradinginfo = grade_get_grades($this->course->id,
694                                         'mod',
695                                         'assign',
696                                         $assign->get_instance()->id,
697                                         $this->extrastudents[0]->id);
699         $this->assertEquals(null, $gradinginfo->items[0]->grades[$this->extrastudents[0]->id]->datesubmitted);
700     }
702     public function test_group_submissions_submit_for_marking() {
703         global $PAGE;
705         $this->create_extra_users();
706         // Now verify group assignments.
707         $this->setUser($this->editingteachers[0]);
708         $assign = $this->create_instance(array('teamsubmission'=>1,
709                                                'assignsubmission_onlinetext_enabled'=>1,
710                                                'submissiondrafts'=>1,
711                                                'requireallteammemberssubmit'=>1));
712         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
714         $this->setUser($this->extrastudents[0]);
715         // Add a submission.
716         $data = new stdClass();
717         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
718                                          'text'=>'Submission text',
719                                          'format'=>FORMAT_MOODLE);
721         $notices = array();
722         $assign->save_submission($data, $notices);
724         // Check we can see the submit button.
725         $output = $assign->view_student_summary($this->extrastudents[0], true);
726         $this->assertContains(get_string('submitassignment', 'assign'), $output);
728         $submission = $assign->get_group_submission($this->extrastudents[0]->id, 0, true);
729         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
730         $assign->testable_update_submission($submission, $this->extrastudents[0]->id, true, true);
732         // Check that the student does not see "Submit" button.
733         $output = $assign->view_student_summary($this->extrastudents[0], true);
734         $this->assertNotContains(get_string('submitassignment', 'assign'), $output);
736         // Change to another user in the same group.
737         $this->setUser($this->extrastudents[self::GROUP_COUNT]);
738         $output = $assign->view_student_summary($this->extrastudents[self::GROUP_COUNT], true);
739         $this->assertContains(get_string('submitassignment', 'assign'), $output);
741         $submission = $assign->get_group_submission($this->extrastudents[self::GROUP_COUNT]->id, 0, true);
742         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
743         $assign->testable_update_submission($submission, $this->extrastudents[self::GROUP_COUNT]->id, true, true);
744         $output = $assign->view_student_summary($this->extrastudents[self::GROUP_COUNT], true);
745         $this->assertNotContains(get_string('submitassignment', 'assign'), $output);
746     }
748     public function test_submissions_open() {
749         $this->setUser($this->editingteachers[0]);
751         $now = time();
752         $tomorrow = $now + 24*60*60;
753         $oneweek = $now + 7*24*60*60;
754         $yesterday = $now - 24*60*60;
756         $assign = $this->create_instance();
757         $this->assertEquals(true, $assign->testable_submissions_open($this->students[0]->id));
759         $assign = $this->create_instance(array('duedate'=>$tomorrow));
760         $this->assertEquals(true, $assign->testable_submissions_open($this->students[0]->id));
762         $assign = $this->create_instance(array('duedate'=>$yesterday));
763         $this->assertEquals(true, $assign->testable_submissions_open($this->students[0]->id));
765         $assign = $this->create_instance(array('duedate'=>$yesterday, 'cutoffdate'=>$tomorrow));
766         $this->assertEquals(true, $assign->testable_submissions_open($this->students[0]->id));
768         $assign = $this->create_instance(array('duedate'=>$yesterday, 'cutoffdate'=>$yesterday));
769         $this->assertEquals(false, $assign->testable_submissions_open($this->students[0]->id));
771         $assign->testable_save_user_extension($this->students[0]->id, $tomorrow);
772         $this->assertEquals(true, $assign->testable_submissions_open($this->students[0]->id));
774         $assign = $this->create_instance(array('submissiondrafts'=>1));
775         $this->assertEquals(true, $assign->testable_submissions_open($this->students[0]->id));
777         $this->setUser($this->students[0]);
778         $now = time();
779         $submission = $assign->get_user_submission($this->students[0]->id, true);
780         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
781         $assign->testable_update_submission($submission, $this->students[0]->id, true, false);
782         $this->setUser($this->editingteachers[0]);
783         $this->assertEquals(false, $assign->testable_submissions_open($this->students[0]->id));
784     }
786     public function test_get_graders() {
787         $this->create_extra_users();
788         $this->setUser($this->editingteachers[0]);
790         // Create an assignment with no groups.
791         $assign = $this->create_instance();
792         $this->assertCount(self::DEFAULT_TEACHER_COUNT +
793                            self::DEFAULT_EDITING_TEACHER_COUNT +
794                            self::EXTRA_TEACHER_COUNT +
795                            self::EXTRA_EDITING_TEACHER_COUNT,
796                            $assign->testable_get_graders($this->students[0]->id));
798         // Force create an assignment with SEPARATEGROUPS.
799         $data = new stdClass();
800         $data->courseid = $this->course->id;
801         $data->name = 'Grouping';
802         $groupingid = groups_create_grouping($data);
803         groups_assign_grouping($groupingid, $this->groups[0]->id);
804         $assign = $this->create_instance(array('groupingid' => $groupingid, 'groupmode' => SEPARATEGROUPS));
806         $this->setUser($this->students[1]);
807         $this->assertCount(4, $assign->testable_get_graders($this->students[0]->id));
808         // Note the second student is in a group that is not in the grouping.
809         // This means that we get all graders that are not in a group in the grouping.
810         $this->assertCount(10, $assign->testable_get_graders($this->students[1]->id));
811     }
813     public function test_group_members_only() {
814         global $CFG;
816         $this->setAdminUser();
817         $this->create_extra_users();
818         $CFG->enablegroupmembersonly = true;
819         $grouping = $this->getDataGenerator()->create_grouping(array('courseid' => $this->course->id));
820         groups_assign_grouping($grouping->id, $this->groups[0]->id);
822         // Force create an assignment with SEPARATEGROUPS.
823         $instance = $this->getDataGenerator()->create_module('assign', array('course'=>$this->course->id),
824             array('groupmembersonly' => SEPARATEGROUPS, 'groupingid' => $grouping->id));
826         $cm = get_coursemodule_from_instance('assign', $instance->id);
827         $context = context_module::instance($cm->id);
828         $assign = new testable_assign($context, $cm, $this->course);
830         $this->setUser($this->teachers[0]);
831         $this->assertCount(5, $assign->list_participants(0, true));
833     }
835     public function test_get_uniqueid_for_user() {
836         $this->setUser($this->editingteachers[0]);
837         $assign = $this->create_instance();
839         foreach ($this->students as $student) {
840             $uniqueid = $assign->get_uniqueid_for_user($student->id);
841             $this->assertEquals($student->id, $assign->get_user_id_for_uniqueid($uniqueid));
842         }
843     }
845     public function test_show_student_summary() {
846         global $CFG, $PAGE;
848         $this->setUser($this->editingteachers[0]);
849         $assign = $this->create_instance();
850         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
852         // No feedback should be available because this student has not been graded.
853         $this->setUser($this->students[0]);
854         $output = $assign->view_student_summary($this->students[0], true);
855         $this->assertEquals(false, strpos($output, 'Feedback'), 'Do not show feedback if there is no grade');
856         // Simulate adding a grade.
857         $this->setUser($this->teachers[0]);
858         $data = new stdClass();
859         $data->grade = '50.0';
860         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
862         // Now we should see the feedback.
863         $this->setUser($this->students[0]);
864         $output = $assign->view_student_summary($this->students[0], true);
865         $this->assertNotEquals(false, strpos($output, 'Feedback'), 'Show feedback if there is a grade');
867         // Now hide the grade in gradebook.
868         $this->setUser($this->teachers[0]);
869         require_once($CFG->libdir.'/gradelib.php');
870         $gradeitem = new grade_item(array(
871             'itemtype'      => 'mod',
872             'itemmodule'    => 'assign',
873             'iteminstance'  => $assign->get_instance()->id,
874             'courseid'      => $this->course->id));
876         $gradeitem->set_hidden(1, false);
878         // No feedback should be available because the grade is hidden.
879         $this->setUser($this->students[0]);
880         $output = $assign->view_student_summary($this->students[0], true);
881         $this->assertEquals(false, strpos($output, 'Feedback'), 'Do not show feedback if the grade is hidden in the gradebook');
883         // Do the same but add feedback.
884         $assign = $this->create_instance(array('assignfeedback_comments_enabled' => 1));
886         $this->setUser($this->teachers[0]);
887         $grade = $assign->get_user_grade($this->students[0]->id, true);
888         $data = new stdClass();
889         $data->assignfeedbackcomments_editor = array('text'=>'Tomato sauce',
890                                          'format'=>FORMAT_MOODLE);
891         $plugin = $assign->get_feedback_plugin_by_type('comments');
892         $plugin->save($grade, $data);
894         // Should have feedback but no grade.
895         $this->setUser($this->students[0]);
896         $output = $assign->view_student_summary($this->students[0], true);
897         $this->assertNotEquals(false, strpos($output, 'Feedback'), 'Show feedback even if there is no grade');
898         $this->assertEquals(false, strpos($output, 'Grade'), 'Do not show grade when there is no grade.');
899         $this->assertEquals(false, strpos($output, 'Graded on'), 'Do not show graded date when there is no grade.');
901         // Now hide the grade in gradebook.
902         $this->setUser($this->teachers[0]);
903         $gradeitem = new grade_item(array(
904             'itemtype'      => 'mod',
905             'itemmodule'    => 'assign',
906             'iteminstance'  => $assign->get_instance()->id,
907             'courseid'      => $this->course->id));
909         $gradeitem->set_hidden(1, false);
911         // No feedback should be available because the grade is hidden.
912         $this->setUser($this->students[0]);
913         $output = $assign->view_student_summary($this->students[0], true);
914         $this->assertEquals(false, strpos($output, 'Feedback'), 'Do not show feedback if the grade is hidden in the gradebook');
915     }
917     public function test_attempt_reopen_method_manual() {
918         global $PAGE;
920         $this->setUser($this->editingteachers[0]);
921         $assign = $this->create_instance(array('attemptreopenmethod'=>ASSIGN_ATTEMPT_REOPEN_METHOD_MANUAL,
922                                                'maxattempts'=>3,
923                                                'submissiondrafts'=>1,
924                                                'assignsubmission_onlinetext_enabled'=>1));
925         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
927         // Student should be able to see an add submission button.
928         $this->setUser($this->students[0]);
929         $output = $assign->view_student_summary($this->students[0], true);
930         $this->assertNotEquals(false, strpos($output, get_string('addsubmission', 'assign')));
932         // Add a submission.
933         $now = time();
934         $submission = $assign->get_user_submission($this->students[0]->id, true);
935         $data = new stdClass();
936         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
937                                          'text'=>'Submission text',
938                                          'format'=>FORMAT_MOODLE);
939         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
940         $plugin->save($submission, $data);
942         // And now submit it for marking.
943         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
944         $assign->testable_update_submission($submission, $this->students[0]->id, true, false);
946         // Verify the student cannot make changes to the submission.
947         $output = $assign->view_student_summary($this->students[0], true);
948         $this->assertEquals(false, strpos($output, get_string('addsubmission', 'assign')));
950         // Mark the submission.
951         $this->setUser($this->teachers[0]);
952         $data = new stdClass();
953         $data->grade = '50.0';
954         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
956         // Check the student can see the grade.
957         $this->setUser($this->students[0]);
958         $output = $assign->view_student_summary($this->students[0], true);
959         $this->assertNotEquals(false, strpos($output, '50.0'));
961         // Allow the student another attempt.
962         $this->teachers[0]->ignoresesskey = true;
963         $this->setUser($this->teachers[0]);
964         $result = $assign->testable_process_add_attempt($this->students[0]->id);
965         $this->assertEquals(true, $result);
967         // Check that the previous attempt is now in the submission history table.
968         $this->setUser($this->students[0]);
969         $output = $assign->view_student_summary($this->students[0], true);
970         // Need a better check.
971         $this->assertNotEquals(false, strpos($output, 'Submission text'), 'Contains: Submission text');
973         // Check that the student now has a button for Add a new attempt".
974         $this->assertNotEquals(false, strpos($output, get_string('addnewattempt', 'assign')));
975         // Check that the student now does not have a button for Submit.
976         $this->assertEquals(false, strpos($output, get_string('submitassignment', 'assign')));
978         // Check that the student now has a submission history.
979         $this->assertNotEquals(false, strpos($output, get_string('attempthistory', 'assign')));
981         $this->setUser($this->teachers[0]);
982         // Check that the grading table loads correctly and contains this user.
983         // This is also testing that we do not get duplicate rows in the grading table.
984         $gradingtable = new assign_grading_table($assign, 100, '', 0, true);
985         $output = $assign->get_renderer()->render($gradingtable);
986         $this->assertEquals(true, strpos($output, $this->students[0]->lastname));
988         // Should be 1 not 2.
989         $this->assertEquals(1, $assign->count_submissions());
990         $this->assertEquals(1, $assign->count_submissions_with_status('reopened'));
991         $this->assertEquals(0, $assign->count_submissions_need_grading());
992         $this->assertEquals(1, $assign->count_grades());
994         // Change max attempts to unlimited.
995         $formdata = clone($assign->get_instance());
996         $formdata->maxattempts = ASSIGN_UNLIMITED_ATTEMPTS;
997         $formdata->instance = $formdata->id;
998         $assign->update_instance($formdata);
1000         // Check we can repopen still.
1001         $result = $assign->testable_process_add_attempt($this->students[0]->id);
1002         $this->assertEquals(true, $result);
1004         $grades = $assign->get_user_grades_for_gradebook($this->students[0]->id);
1005         $this->assertEquals(50, (int)$grades[$this->students[0]->id]->rawgrade);
1007     }
1009     public function test_markingworkflow() {
1010         global $PAGE;
1012         $this->setUser($this->editingteachers[0]);
1013         $assign = $this->create_instance(array('markingworkflow'=>1));
1014         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
1016         // Mark the submission and set to notmarked.
1017         $this->setUser($this->teachers[0]);
1018         $data = new stdClass();
1019         $data->grade = '50.0';
1020         $data->workflowstate = ASSIGN_MARKING_WORKFLOW_STATE_NOTMARKED;
1021         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
1023         // Check the student can't see the grade.
1024         $this->setUser($this->students[0]);
1025         $output = $assign->view_student_summary($this->students[0], true);
1026         $this->assertEquals(false, strpos($output, '50.0'));
1028         // Mark the submission and set to inmarking.
1029         $this->setUser($this->teachers[0]);
1030         $data = new stdClass();
1031         $data->grade = '50.0';
1032         $data->workflowstate = ASSIGN_MARKING_WORKFLOW_STATE_INMARKING;
1033         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
1035         // Check the student can't see the grade.
1036         $this->setUser($this->students[0]);
1037         $output = $assign->view_student_summary($this->students[0], true);
1038         $this->assertEquals(false, strpos($output, '50.0'));
1040         // Mark the submission and set to readyforreview.
1041         $this->setUser($this->teachers[0]);
1042         $data = new stdClass();
1043         $data->grade = '50.0';
1044         $data->workflowstate = ASSIGN_MARKING_WORKFLOW_STATE_READYFORREVIEW;
1045         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
1047         // Check the student can't see the grade.
1048         $this->setUser($this->students[0]);
1049         $output = $assign->view_student_summary($this->students[0], true);
1050         $this->assertEquals(false, strpos($output, '50.0'));
1052         // Mark the submission and set to inreview.
1053         $this->setUser($this->teachers[0]);
1054         $data = new stdClass();
1055         $data->grade = '50.0';
1056         $data->workflowstate = ASSIGN_MARKING_WORKFLOW_STATE_INREVIEW;
1057         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
1059         // Check the student can't see the grade.
1060         $this->setUser($this->students[0]);
1061         $output = $assign->view_student_summary($this->students[0], true);
1062         $this->assertEquals(false, strpos($output, '50.0'));
1064         // Mark the submission and set to readyforrelease.
1065         $this->setUser($this->teachers[0]);
1066         $data = new stdClass();
1067         $data->grade = '50.0';
1068         $data->workflowstate = ASSIGN_MARKING_WORKFLOW_STATE_READYFORRELEASE;
1069         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
1071         // Check the student can't see the grade.
1072         $this->setUser($this->students[0]);
1073         $output = $assign->view_student_summary($this->students[0], true);
1074         $this->assertEquals(false, strpos($output, '50.0'));
1076         // Mark the submission and set to released.
1077         $this->setUser($this->teachers[0]);
1078         $data = new stdClass();
1079         $data->grade = '50.0';
1080         $data->workflowstate = ASSIGN_MARKING_WORKFLOW_STATE_RELEASED;
1081         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
1083         // Check the student can see the grade.
1084         $this->setUser($this->students[0]);
1085         $output = $assign->view_student_summary($this->students[0], true);
1086         $this->assertNotEquals(false, strpos($output, '50.0'));
1087     }
1089     public function test_markerallocation() {
1090         global $PAGE;
1092         $this->setUser($this->editingteachers[0]);
1093         $assign = $this->create_instance(array('markingworkflow'=>1, 'markingallocation'=>1));
1094         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
1096         // Allocate marker to submission.
1097         $data = new stdClass();
1098         $data->allocatedmarker = $this->teachers[0]->id;
1099         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
1101         // Check the allocated marker can view the submission.
1102         $this->setUser($this->teachers[0]);
1103         $gradingtable = new assign_grading_table($assign, 100, '', 0, true);
1104         $output = $assign->get_renderer()->render($gradingtable);
1105         $this->assertEquals(true, strpos($output, $this->students[0]->lastname));
1107         // Check that other teachers can't view this submission.
1108         $this->setUser($this->teachers[1]);
1109         $gradingtable = new assign_grading_table($assign, 100, '', 0, true);
1110         $output = $assign->get_renderer()->render($gradingtable);
1111         $this->assertNotEquals(true, strpos($output, $this->students[0]->lastname));
1112     }
1114     public function test_extension_granted_event() {
1115         $this->setUser($this->editingteachers[0]);
1117         $tomorrow = time() + 24*60*60;
1118         $yesterday = time() - 24*60*60;
1120         $assign = $this->create_instance(array('duedate' => $yesterday, 'cutoffdate' => $yesterday));
1121         $sink = $this->redirectEvents();
1123         $assign->testable_save_user_extension($this->students[0]->id, $tomorrow);
1125         $events = $sink->get_events();
1126         $this->assertCount(1, $events);
1127         $event = reset($events);
1128         $this->assertInstanceOf('\mod_assign\event\extension_granted', $event);
1129         $this->assertEquals($assign->get_context(), $event->get_context());
1130         $this->assertEquals($assign->get_instance()->id, $event->objectid);
1131         $this->assertEquals($this->students[0]->id, $event->relateduserid);
1132         $expected = array(
1133             $assign->get_course()->id,
1134             'assign',
1135             'grant extension',
1136             'view.php?id=' . $assign->get_course_module()->id,
1137             $this->students[0]->id,
1138             $assign->get_course_module()->id,
1139             $this->editingteachers[0]->id
1140         );
1141         $this->assertEventLegacyLogData($expected, $event);
1142         $sink->close();
1143     }
1145     public function test_submission_locked_event() {
1146         $this->editingteachers[0]->ignoresesskey = true;
1147         $this->setUser($this->editingteachers[0]);
1149         $assign = $this->create_instance();
1150         $sink = $this->redirectEvents();
1152         $assign->lock_submission($this->students[0]->id);
1154         $events = $sink->get_events();
1155         $this->assertCount(1, $events);
1156         $event = reset($events);
1157         $this->assertInstanceOf('\mod_assign\event\submission_locked', $event);
1158         $this->assertEquals($assign->get_context(), $event->get_context());
1159         $this->assertEquals($assign->get_instance()->id, $event->objectid);
1160         $this->assertEquals($this->students[0]->id, $event->relateduserid);
1161         $expected = array(
1162             $assign->get_course()->id,
1163             'assign',
1164             'lock submission',
1165             'view.php?id=' . $assign->get_course_module()->id,
1166             get_string('locksubmissionforstudent', 'assign', array('id' => $this->students[0]->id,
1167                 'fullname' => fullname($this->students[0]))),
1168             $assign->get_course_module()->id,
1169             $this->editingteachers[0]->id
1170         );
1171         $this->assertEventLegacyLogData($expected, $event);
1172         $sink->close();
1174         // Revert to defaults.
1175         $this->editingteachers[0]->ignoresesskey = false;
1176     }
1178     public function test_identities_revealed_event() {
1179         $this->editingteachers[0]->ignoresesskey = true;
1180         $this->setUser($this->editingteachers[0]);
1182         $assign = $this->create_instance(array('blindmarking'=>1));
1183         $sink = $this->redirectEvents();
1185         $assign->reveal_identities();
1187         $events = $sink->get_events();
1188         $this->assertCount(1, $events);
1189         $event = reset($events);
1190         $this->assertInstanceOf('\mod_assign\event\identities_revealed', $event);
1191         $this->assertEquals($assign->get_context(), $event->get_context());
1192         $this->assertEquals($assign->get_instance()->id, $event->objectid);
1193         $expected = array(
1194             $assign->get_course()->id,
1195             'assign',
1196             'reveal identities',
1197             'view.php?id=' . $assign->get_course_module()->id,
1198             get_string('revealidentities', 'assign'),
1199             $assign->get_course_module()->id,
1200             $this->editingteachers[0]->id
1201         );
1202         $this->assertEventLegacyLogData($expected, $event);
1203         $sink->close();
1205         // Revert to defaults.
1206         $this->editingteachers[0]->ignoresesskey = false;
1207     }
1209     public function test_submission_status_updated_event() {
1210         $this->editingteachers[0]->ignoresesskey = true;
1211         $this->setUser($this->editingteachers[0]);
1213         $assign = $this->create_instance();
1214         $submission = $assign->get_user_submission($this->students[0]->id, true);
1215         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
1216         $assign->testable_update_submission($submission, $this->students[0]->id, true, false);
1218         $sink = $this->redirectEvents();
1219         $assign->revert_to_draft($this->students[0]->id);
1221         $events = $sink->get_events();
1222         $this->assertCount(1, $events);
1223         $event = reset($events);
1224         $this->assertInstanceOf('\mod_assign\event\submission_status_updated', $event);
1225         $this->assertEquals($assign->get_context(), $event->get_context());
1226         $this->assertEquals($submission->id, $event->objectid);
1227         $this->assertEquals($this->students[0]->id, $event->relateduserid);
1228         $this->assertEquals(ASSIGN_SUBMISSION_STATUS_DRAFT, $event->other['newstatus']);
1229         $expected = array(
1230             $assign->get_course()->id,
1231             'assign',
1232             'revert submission to draft',
1233             'view.php?id=' . $assign->get_course_module()->id,
1234             get_string('reverttodraftforstudent', 'assign', array('id' => $this->students[0]->id,
1235                 'fullname' => fullname($this->students[0]))),
1236             $assign->get_course_module()->id,
1237             $this->editingteachers[0]->id
1238         );
1239         $this->assertEventLegacyLogData($expected, $event);
1240         $sink->close();
1242         // Revert to defaults.
1243         $this->editingteachers[0]->ignoresesskey = false;
1244     }
1246     public function test_marker_updated_event() {
1247         $this->editingteachers[0]->ignoresesskey = true;
1248         $this->setUser($this->editingteachers[0]);
1250         $assign = $this->create_instance();
1252         $sink = $this->redirectEvents();
1253         $assign->testable_process_set_batch_marking_allocation($this->students[0]->id, $this->teachers[0]->id);
1255         $events = $sink->get_events();
1256         $this->assertCount(1, $events);
1257         $event = reset($events);
1258         $this->assertInstanceOf('\mod_assign\event\marker_updated', $event);
1259         $this->assertEquals($assign->get_context(), $event->get_context());
1260         $this->assertEquals($assign->get_instance()->id, $event->objectid);
1261         $this->assertEquals($this->students[0]->id, $event->relateduserid);
1262         $this->assertEquals($this->editingteachers[0]->id, $event->userid);
1263         $this->assertEquals($this->teachers[0]->id, $event->other['markerid']);
1264         $expected = array(
1265             $assign->get_course()->id,
1266             'assign',
1267             'set marking allocation',
1268             'view.php?id=' . $assign->get_course_module()->id,
1269             get_string('setmarkerallocationforlog', 'assign', array('id' => $this->students[0]->id,
1270                 'fullname' => fullname($this->students[0]), 'marker' => fullname($this->teachers[0]))),
1271             $assign->get_course_module()->id,
1272             $this->editingteachers[0]->id
1273         );
1274         $this->assertEventLegacyLogData($expected, $event);
1275         $sink->close();
1277         // Revert to defaults.
1278         $this->editingteachers[0]->ignoresesskey = false;
1279     }
1281     public function test_workflow_state_updated_event() {
1282         $this->editingteachers[0]->ignoresesskey = true;
1283         $this->setUser($this->editingteachers[0]);
1285         $assign = $this->create_instance();
1287         $sink = $this->redirectEvents();
1288         $assign->testable_process_set_batch_marking_workflow_state($this->students[0]->id, ASSIGN_MARKING_WORKFLOW_STATE_INREVIEW);
1290         $events = $sink->get_events();
1291         $this->assertCount(1, $events);
1292         $event = reset($events);
1293         $this->assertInstanceOf('\mod_assign\event\workflow_state_updated', $event);
1294         $this->assertEquals($assign->get_context(), $event->get_context());
1295         $this->assertEquals($assign->get_instance()->id, $event->objectid);
1296         $this->assertEquals($this->students[0]->id, $event->relateduserid);
1297         $this->assertEquals($this->editingteachers[0]->id, $event->userid);
1298         $this->assertEquals(ASSIGN_MARKING_WORKFLOW_STATE_INREVIEW, $event->other['newstate']);
1299         $expected = array(
1300             $assign->get_course()->id,
1301             'assign',
1302             'set marking workflow state',
1303             'view.php?id=' . $assign->get_course_module()->id,
1304             get_string('setmarkingworkflowstateforlog', 'assign', array('id' => $this->students[0]->id,
1305                 'fullname' => fullname($this->students[0]), 'state' => ASSIGN_MARKING_WORKFLOW_STATE_INREVIEW)),
1306             $assign->get_course_module()->id,
1307             $this->editingteachers[0]->id
1308         );
1309         $this->assertEventLegacyLogData($expected, $event);
1310         $sink->close();
1312         // Revert to defaults.
1313         $this->editingteachers[0]->ignoresesskey = false;
1314     }
1316     public function test_submission_duplicated_event() {
1317         $this->setUser($this->students[0]);
1319         $assign = $this->create_instance();
1320         $submission1 = $assign->get_user_submission($this->students[0]->id, true, 0);
1321         $submission2 = $assign->get_user_submission($this->students[0]->id, true, 1);
1322         $submission2->status = ASSIGN_SUBMISSION_STATUS_REOPENED;
1323         $assign->testable_update_submission($submission2, $this->students[0]->id, time(), $assign->get_instance()->teamsubmission);
1325         $sink = $this->redirectEvents();
1326         $notices = null;
1327         $assign->copy_previous_attempt($notices);
1329         $events = $sink->get_events();
1330         $this->assertCount(1, $events);
1331         $event = reset($events);
1332         $this->assertInstanceOf('\mod_assign\event\submission_duplicated', $event);
1333         $this->assertEquals($assign->get_context(), $event->get_context());
1334         $this->assertEquals($submission2->id, $event->objectid);
1335         $this->assertEquals($this->students[0]->id, $event->userid);
1336         $submission2->status = ASSIGN_SUBMISSION_STATUS_DRAFT;
1337         $expected = array(
1338             $assign->get_course()->id,
1339             'assign',
1340             'submissioncopied',
1341             'view.php?id=' . $assign->get_course_module()->id,
1342             $assign->testable_format_submission_for_log($submission2),
1343             $assign->get_course_module()->id,
1344             $this->students[0]->id
1345         );
1346         $this->assertEventLegacyLogData($expected, $event);
1347         $sink->close();
1348     }
1350     public function test_submission_unlocked_event() {
1351         $this->editingteachers[0]->ignoresesskey = true;
1352         $this->setUser($this->editingteachers[0]);
1354         $assign = $this->create_instance();
1355         $sink = $this->redirectEvents();
1357         $assign->unlock_submission($this->students[0]->id);
1359         $events = $sink->get_events();
1360         $this->assertCount(1, $events);
1361         $event = reset($events);
1362         $this->assertInstanceOf('\mod_assign\event\submission_unlocked', $event);
1363         $this->assertEquals($assign->get_context(), $event->get_context());
1364         $this->assertEquals($assign->get_instance()->id, $event->objectid);
1365         $this->assertEquals($this->students[0]->id, $event->relateduserid);
1366         $expected = array(
1367             $assign->get_course()->id,
1368             'assign',
1369             'unlock submission',
1370             'view.php?id=' . $assign->get_course_module()->id,
1371             get_string('unlocksubmissionforstudent', 'assign', array('id' => $this->students[0]->id,
1372                 'fullname' => fullname($this->students[0]))),
1373             $assign->get_course_module()->id,
1374             $this->editingteachers[0]->id
1375         );
1376         $this->assertEventLegacyLogData($expected, $event);
1377         $sink->close();
1379         // Revert to defaults.
1380         $this->editingteachers[0]->ignoresesskey = false;
1381     }
1383     public function test_submission_graded_event() {
1384         $this->setUser($this->editingteachers[0]);
1385         $assign = $this->create_instance();
1387         // Test apply_grade_to_user.
1388         $sink = $this->redirectEvents();
1390         $data = new stdClass();
1391         $data->grade = '50.0';
1392         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
1393         $grade = $assign->get_user_grade($this->students[0]->id, false, 0);
1395         $events = $sink->get_events();
1396         $this->assertCount(1, $events);
1397         $event = reset($events);
1398         $this->assertInstanceOf('\mod_assign\event\submission_graded', $event);
1399         $this->assertEquals($assign->get_context(), $event->get_context());
1400         $this->assertEquals($grade->id, $event->objectid);
1401         $this->assertEquals($this->students[0]->id, $event->relateduserid);
1402         $expected = array(
1403             $assign->get_course()->id,
1404             'assign',
1405             'grade submission',
1406             'view.php?id=' . $assign->get_course_module()->id,
1407             $assign->format_grade_for_log($grade),
1408             $assign->get_course_module()->id,
1409             $this->editingteachers[0]->id
1410         );
1411         $this->assertEventLegacyLogData($expected, $event);
1412         $sink->close();
1414         // Test process_save_quick_grades.
1415         $sink = $this->redirectEvents();
1417         $data = array(
1418             'grademodified_' . $this->students[0]->id => time(),
1419             'quickgrade_' . $this->students[0]->id => '60.0'
1420         );
1421         $assign->testable_process_save_quick_grades($data);
1422         $grade = $assign->get_user_grade($this->students[0]->id, false);
1423         $this->assertEquals('60.0', $grade->grade);
1425         $events = $sink->get_events();
1426         $this->assertCount(1, $events);
1427         $event = reset($events);
1428         $this->assertInstanceOf('\mod_assign\event\submission_graded', $event);
1429         $this->assertEquals($assign->get_context(), $event->get_context());
1430         $this->assertEquals($grade->id, $event->objectid);
1431         $this->assertEquals($this->students[0]->id, $event->relateduserid);
1432         $expected = array(
1433             $assign->get_course()->id,
1434             'assign',
1435             'grade submission',
1436             'view.php?id=' . $assign->get_course_module()->id,
1437             $assign->format_grade_for_log($grade),
1438             $assign->get_course_module()->id,
1439             $this->editingteachers[0]->id
1440         );
1441         $this->assertEventLegacyLogData($expected, $event);
1442         $sink->close();
1443     }
1445     public function test_disable_submit_after_cutoff_date() {
1446         global $PAGE;
1448         $this->setUser($this->editingteachers[0]);
1449         $now = time();
1450         $tomorrow = $now + 24*60*60;
1451         $lastweek = $now - 7*24*60*60;
1452         $yesterday = $now - 24*60*60;
1454         $assign = $this->create_instance(array('duedate'=>$yesterday,
1455                                                'cutoffdate'=>$tomorrow,
1456                                                'assignsubmission_onlinetext_enabled'=>1));
1457         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
1459         // Student should be able to see an add submission button.
1460         $this->setUser($this->students[0]);
1461         $output = $assign->view_student_summary($this->students[0], true);
1462         $this->assertNotEquals(false, strpos($output, get_string('addsubmission', 'assign')));
1464         // Add a submission but don't submit now.
1465         $submission = $assign->get_user_submission($this->students[0]->id, true);
1466         $data = new stdClass();
1467         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
1468                                          'text'=>'Submission text',
1469                                          'format'=>FORMAT_MOODLE);
1470         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
1471         $plugin->save($submission, $data);
1473         // Create another instance with cut-off and due-date already passed.
1474         $this->setUser($this->editingteachers[0]);
1475         $now = time();
1476         $assign = $this->create_instance(array('duedate'=>$lastweek,
1477                                                'cutoffdate'=>$yesterday,
1478                                                'assignsubmission_onlinetext_enabled'=>1));
1480         $this->setUser($this->students[0]);
1481         $output = $assign->view_student_summary($this->students[0], true);
1482         $this->assertNotContains($output, get_string('editsubmission', 'assign'),
1483                                  'Should not be able to edit after cutoff date.');
1484         $this->assertNotContains($output, get_string('submitassignment', 'assign'),
1485                                  'Should not be able to submit after cutoff date.');
1486     }
1487     /**
1488      * Testing for submission comment plugin settings
1489      */
1490     public function test_submission_comment_plugin_settings() {
1491         global $CFG;
1493         $commentconfig = false;
1494         if (!empty($CFG->usecomments)) {
1495             $commentconfig = $CFG->usecomments;
1496         }
1498         $CFG->usecomments = true;
1499         $assign = $this->create_instance();
1500         $plugin = $assign->get_submission_plugin_by_type('comments');
1501         $this->assertEquals(1, $plugin->is_enabled('enabled'));
1503         $assign = $this->create_instance(array('assignsubmission_comments_enabled' => 0));
1504         $plugin = $assign->get_submission_plugin_by_type('comments');
1505         $this->assertEquals(1, $plugin->is_enabled('enabled'));
1507         $assign = $this->create_instance(array('assignsubmission_comments_enabled' => 1));
1508         $plugin = $assign->get_submission_plugin_by_type('comments');
1509         $this->assertEquals(1, $plugin->is_enabled('enabled'));
1511         $CFG->usecomments = false;
1512         $assign = $this->create_instance();
1513         $plugin = $assign->get_submission_plugin_by_type('comments');
1514         $this->assertEquals(0, $plugin->is_enabled('enabled'));
1516         $assign = $this->create_instance(array('assignsubmission_comments_enabled' => 0));
1517         $plugin = $assign->get_submission_plugin_by_type('comments');
1518         $this->assertEquals(0, $plugin->is_enabled('enabled'));
1520         $assign = $this->create_instance(array('assignsubmission_comments_enabled' => 1));
1521         $plugin = $assign->get_submission_plugin_by_type('comments');
1522         $this->assertEquals(0, $plugin->is_enabled('enabled'));
1524         $CFG->usecomments = $commentconfig;
1525     }
1527     /**
1528      * Testing for comment inline settings
1529      */
1530     public function test_feedback_comment_commentinline() {
1531         global $CFG;
1533         $sourcetext = "Hello!
1535 I'm writing to you from the Moodle Majlis in Muscat, Oman, where we just had several days of Moodle community goodness.
1537 URL outside a tag: https://moodle.org/logo/logo-240x60.gif
1538 Plugin url outside a tag: @@PLUGINFILE@@/logo-240x60.gif
1540 External link 1:<img src='https://moodle.org/logo/logo-240x60.gif' alt='Moodle'/>
1541 External link 2:<img alt=\"Moodle\" src=\"https://moodle.org/logo/logo-240x60.gif\"/>
1542 Internal link 1:<img src='@@PLUGINFILE@@/logo-240x60.gif' alt='Moodle'/>
1543 Internal link 2:<img alt=\"Moodle\" src=\"@@PLUGINFILE@@logo-240x60.gif\"/>
1544 Anchor link 1:<a href=\"@@PLUGINFILE@@logo-240x60.gif\" alt=\"bananas\">Link text</a>
1545 Anchor link 2:<a title=\"bananas\" href=\"../logo-240x60.gif\">Link text</a>
1546 ";
1548         // Note the internal images have been stripped and the html is purified (quotes fixed in this case).
1549         $filteredtext = "Hello!
1551 I'm writing to you from the Moodle Majlis in Muscat, Oman, where we just had several days of Moodle community goodness.
1553 URL outside a tag: https://moodle.org/logo/logo-240x60.gif
1554 Plugin url outside a tag: @@PLUGINFILE@@/logo-240x60.gif
1556 External link 1:<img src=\"https://moodle.org/logo/logo-240x60.gif\" alt=\"Moodle\" />
1557 External link 2:<img alt=\"Moodle\" src=\"https://moodle.org/logo/logo-240x60.gif\" />
1558 Internal link 1:
1559 Internal link 2:
1560 Anchor link 1:Link text
1561 Anchor link 2:<a title=\"bananas\" href=\"../logo-240x60.gif\">Link text</a>
1562 ";
1564         $this->setUser($this->editingteachers[0]);
1565         $params = array('assignsubmission_onlinetext_enabled' => 1,
1566                         'assignfeedback_comments_enabled' => 1,
1567                         'assignfeedback_comments_commentinline' => 1);
1568         $assign = $this->create_instance($params);
1570         $this->setUser($this->students[0]);
1571         // Add a submission but don't submit now.
1572         $submission = $assign->get_user_submission($this->students[0]->id, true);
1573         $data = new stdClass();
1575         // Test the internal link is stripped, but the external one is not.
1576         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
1577                                          'text'=>$sourcetext,
1578                                          'format'=>FORMAT_MOODLE);
1580         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
1581         $plugin->save($submission, $data);
1583         $this->setUser($this->editingteachers[0]);
1585         $data = new stdClass();
1586         require_once($CFG->dirroot . '/mod/assign/gradeform.php');
1587         $pagination = array('userid'=>$this->students[0]->id,
1588                             'rownum'=>0,
1589                             'last'=>true,
1590                             'useridlistid'=>time(),
1591                             'attemptnumber'=>0);
1592         $formparams = array($assign, $data, $pagination);
1593         $mform = new mod_assign_grade_form(null, $formparams);
1595         $this->assertEquals($filteredtext, $data->assignfeedbackcomments_editor['text']);
1596     }
1598     /**
1599      * Testing for feedback comment plugin settings
1600      */
1601     public function test_feedback_plugin_settings() {
1603         $assign = $this->create_instance();
1604         $plugin = $assign->get_feedback_plugin_by_type('comments');
1605         $this->assertEquals(0, $plugin->is_enabled('enabled'));
1607         $assign = $this->create_instance(array('assignfeedback_comments_enabled' => 0));
1608         $plugin = $assign->get_feedback_plugin_by_type('comments');
1609         $this->assertEquals(0, $plugin->is_enabled('enabled'));
1611         $assign = $this->create_instance(array('assignfeedback_comments_enabled' => 1));
1612         $plugin = $assign->get_feedback_plugin_by_type('comments');
1613         $this->assertEquals(1, $plugin->is_enabled('enabled'));
1614     }