Merge branch 'wip-MDL-48347-master' of git://github.com/abgreeve/moodle
[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     /**
122      * Test submissions with extension date.
123      */
124     public function test_gradingtable_extension_due_date() {
125         global $PAGE;
127         // Setup the assignment.
128         $this->create_extra_users();
129         $this->setUser($this->editingteachers[0]);
130         $assign = $this->create_instance(array(
131             'assignsubmission_onlinetext_enabled'=>1,
132             'duedate' => time() - 4 * 24 * 60 * 60,
133          ));
134         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array(
135             'id' => $assign->get_course_module()->id,
136             'action' => 'grading',
137         )));
139         // Check that the assignment is late.
140         $gradingtable = new assign_grading_table($assign, 1, '', 0, true);
141         $output = $assign->get_renderer()->render($gradingtable);
142         $this->assertContains(get_string('submissionstatus_', 'assign'), $output);
143         $this->assertContains(get_string('overdue', 'assign', format_time(4*24*60*60)), $output);
145         // Grant an extension.
146         $extendedtime = time() + 2 * 24 * 60 * 60;
147         $assign->testable_save_user_extension($this->students[0]->id, $extendedtime);
148         $gradingtable = new assign_grading_table($assign, 1, '', 0, true);
149         $output = $assign->get_renderer()->render($gradingtable);
150         $this->assertContains(get_string('submissionstatus_', 'assign'), $output);
151         $this->assertContains(get_string('userextensiondate', 'assign', userdate($extendedtime)), $output);
153         // Simulate a submission.
154         $this->setUser($this->students[0]);
155         $submission = $assign->get_user_submission($this->students[0]->id, true);
156         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
157         $assign->testable_update_submission($submission, $this->students[0]->id, true, false);
158         $data = new stdClass();
159         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
160                                          'text'=>'Submission text',
161                                          'format'=>FORMAT_MOODLE);
162         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
163         $plugin->save($submission, $data);
165         // Verify output.
166         $this->setUser($this->editingteachers[0]);
167         $gradingtable = new assign_grading_table($assign, 1, '', 0, true);
168         $output = $assign->get_renderer()->render($gradingtable);
169         $this->assertContains(get_string('submissionstatus_submitted', 'assign'), $output);
170         $this->assertContains(get_string('userextensiondate', 'assign', userdate($extendedtime)), $output);
171     }
173     /**
174      * Test that late submissions with extension date calculate correctly.
175      */
176     public function test_gradingtable_extension_date_calculation_for_lateness() {
177         global $PAGE;
179         // Setup the assignment.
180         $this->create_extra_users();
181         $this->setUser($this->editingteachers[0]);
182         $time = time();
183         $assign = $this->create_instance(array(
184             'assignsubmission_onlinetext_enabled'=>1,
185             'duedate' => $time - 4 * 24 * 60 * 60,
186          ));
187         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array(
188             'id' => $assign->get_course_module()->id,
189             'action' => 'grading',
190         )));
192         // Check that the assignment is late.
193         $gradingtable = new assign_grading_table($assign, 1, '', 0, true);
194         $output = $assign->get_renderer()->render($gradingtable);
195         $this->assertContains(get_string('submissionstatus_', 'assign'), $output);
196         $difftime = time() - $time;
197         $this->assertContains(get_string('overdue', 'assign', format_time(4*24*60*60 + $difftime)), $output);
199         // Grant an extension that is in the past.
200         $assign->testable_save_user_extension($this->students[0]->id, $time - 2 * 24 * 60 * 60);
201         $gradingtable = new assign_grading_table($assign, 1, '', 0, true);
202         $output = $assign->get_renderer()->render($gradingtable);
203         $this->assertContains(get_string('submissionstatus_', 'assign'), $output);
204         $this->assertContains(get_string('userextensiondate', 'assign', userdate($time - 2*24*60*60)), $output);
205         $difftime = time() - $time;
206         $this->assertContains(get_string('overdue', 'assign', format_time(2*24*60*60 + $difftime)), $output);
208         // Simulate a submission.
209         $this->setUser($this->students[0]);
210         $submission = $assign->get_user_submission($this->students[0]->id, true);
211         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
212         $assign->testable_update_submission($submission, $this->students[0]->id, true, false);
213         $data = new stdClass();
214         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
215                                          'text'=>'Submission text',
216                                          'format'=>FORMAT_MOODLE);
217         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
218         $plugin->save($submission, $data);
219         $submittedtime = time();
221         // Verify output.
222         $this->setUser($this->editingteachers[0]);
223         $gradingtable = new assign_grading_table($assign, 1, '', 0, true);
224         $output = $assign->get_renderer()->render($gradingtable);
225         $this->assertContains(get_string('submissionstatus_submitted', 'assign'), $output);
226         $this->assertContains(get_string('userextensiondate', 'assign', userdate($time - 2*24*60*60)), $output);
228         $difftime = $submittedtime - $time;
229         $this->assertContains(get_string('submittedlateshort', 'assign', format_time(2*24*60*60 + $difftime)), $output);
230     }
232     /**
233      * Check that group submission information is rendered correctly in the
234      * grading table.
235      */
236     public function test_gradingtable_group_submissions_rendering() {
237         global $PAGE;
239         $this->create_extra_users();
240         // Now verify group assignments.
241         $this->setUser($this->teachers[0]);
242         $assign = $this->create_instance(array(
243             'teamsubmission' => 1,
244             'assignsubmission_onlinetext_enabled' => 1,
245             'submissiondrafts' => 1,
246             'requireallteammemberssubmit' => 0,
247         ));
248         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array(
249             'id' => $assign->get_course_module()->id,
250             'action' => 'grading',
251         )));
253         // Add a submission.
254         $this->setUser($this->extrastudents[0]);
255         $data = new stdClass();
256         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
257                                          'text'=>'Submission text',
258                                          'format'=>FORMAT_MOODLE);
259         $notices = array();
260         $assign->save_submission($data, $notices);
262         $submission = $assign->get_group_submission($this->extrastudents[0]->id, 0, true);
263         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
264         $assign->testable_update_submission($submission, $this->extrastudents[0]->id, true, true);
266         // Check output.
267         $this->setUser($this->teachers[0]);
268         $gradingtable = new assign_grading_table($assign, 4, '', 0, true);
269         $output = $assign->get_renderer()->render($gradingtable);
270         $document = new DOMDocument();
271         $document->loadHTML($output);
272         $xpath = new DOMXPath($document);
274         // Check status.
275         $this->assertSame(get_string('submissionstatus_submitted', 'assign'), $xpath->evaluate('string(//td[@id="mod_assign_grading_r0_c4"]/div[@class="submissionstatussubmitted"])'));
276         $this->assertSame(get_string('submissionstatus_submitted', 'assign'), $xpath->evaluate('string(//td[@id="mod_assign_grading_r3_c4"]/div[@class="submissionstatussubmitted"])'));
278         // Check submission last modified date
279         $this->assertGreaterThan(0, strtotime($xpath->evaluate('string(//td[@id="mod_assign_grading_r0_c8"])')));
280         $this->assertGreaterThan(0, strtotime($xpath->evaluate('string(//td[@id="mod_assign_grading_r3_c8"])')));
282         // Check group.
283         $this->assertSame($this->groups[0]->name, $xpath->evaluate('string(//td[@id="mod_assign_grading_r0_c5"])'));
284         $this->assertSame($this->groups[0]->name, $xpath->evaluate('string(//td[@id="mod_assign_grading_r3_c5"])'));
286         // Check submission text.
287         $this->assertSame('Submission text', $xpath->evaluate('string(//td[@id="mod_assign_grading_r0_c9"]/div/div)'));
288         $this->assertSame('Submission text', $xpath->evaluate('string(//td[@id="mod_assign_grading_r3_c9"]/div/div)'));
290         // Check comments can be made.
291         $this->assertSame(1, (int)$xpath->evaluate('count(//td[@id="mod_assign_grading_r0_c10"]//textarea)'));
292         $this->assertSame(1, (int)$xpath->evaluate('count(//td[@id="mod_assign_grading_r3_c10"]//textarea)'));
293     }
295     public function test_show_intro() {
296         // Test whether we are showing the intro at the correct times.
297         $this->setUser($this->editingteachers[0]);
298         $assign = $this->create_instance(array('alwaysshowdescription'=>1));
300         $this->assertEquals(true, $assign->testable_show_intro());
302         $tomorrow = time() + (24*60*60);
304         $assign = $this->create_instance(array('alwaysshowdescription'=>0,
305                                                'allowsubmissionsfromdate'=>$tomorrow));
306         $this->assertEquals(false, $assign->testable_show_intro());
307         $yesterday = time() - (24*60*60);
308         $assign = $this->create_instance(array('alwaysshowdescription'=>0,
309                                                'allowsubmissionsfromdate'=>$yesterday));
310         $this->assertEquals(true, $assign->testable_show_intro());
311     }
313     public function test_has_submissions_or_grades() {
314         $this->setUser($this->editingteachers[0]);
315         $assign = $this->create_instance(array('assignsubmission_onlinetext_enabled'=>1));
317         $instance = $assign->get_instance();
319         // Should start empty.
320         $this->assertEquals(false, $assign->has_submissions_or_grades());
322         // Simulate a submission.
323         $this->setUser($this->students[0]);
324         $submission = $assign->get_user_submission($this->students[0]->id, true);
326         // The submission is still new.
327         $this->assertEquals(false, $assign->has_submissions_or_grades());
329         // Submit the submission.
330         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
331         $assign->testable_update_submission($submission, $this->students[0]->id, true, false);
332         $data = new stdClass();
333         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
334                                          'text'=>'Submission text',
335                                          'format'=>FORMAT_MOODLE);
336         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
337         $plugin->save($submission, $data);
339         // Now test again.
340         $this->assertEquals(true, $assign->has_submissions_or_grades());
341         // Set this back to default.
342         $this->students[0]->ignoresesskey = false;
343     }
345     public function test_delete_grades() {
346         $this->setUser($this->editingteachers[0]);
347         $assign = $this->create_instance();
349         // Simulate adding a grade.
350         $this->setUser($this->teachers[0]);
351         $data = new stdClass();
352         $data->grade = '50.0';
353         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
355         // Now see if the data is in the gradebook.
356         $gradinginfo = grade_get_grades($this->course->id,
357                                         'mod',
358                                         'assign',
359                                         $assign->get_instance()->id);
361         $this->assertNotEquals(0, count($gradinginfo->items));
363         $assign->testable_delete_grades();
364         $gradinginfo = grade_get_grades($this->course->id,
365                                         'mod',
366                                         'assign',
367                                         $assign->get_instance()->id);
369         $this->assertEquals(0, count($gradinginfo->items));
370     }
372     public function test_delete_instance() {
373         $this->setUser($this->editingteachers[0]);
374         $assign = $this->create_instance(array('assignsubmission_onlinetext_enabled'=>1));
376         // Simulate adding a grade.
377         $this->setUser($this->teachers[0]);
378         $data = new stdClass();
379         $data->grade = '50.0';
380         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
382         // Simulate a submission.
383         $this->setUser($this->students[0]);
384         $submission = $assign->get_user_submission($this->students[0]->id, true);
385         $data = new stdClass();
386         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
387                                          'text'=>'Submission text',
388                                          'format'=>FORMAT_MOODLE);
389         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
390         $plugin->save($submission, $data);
392         // Now try and delete.
393         $this->assertEquals(true, $assign->delete_instance());
394     }
396     public function test_reset_userdata() {
397         global $DB;
399         $now = time();
400         $this->setUser($this->editingteachers[0]);
401         $assign = $this->create_instance(array('assignsubmission_onlinetext_enabled'=>1,
402                                                'duedate'=>$now));
404         // Simulate adding a grade.
405         $this->setUser($this->teachers[0]);
406         $data = new stdClass();
407         $data->grade = '50.0';
408         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
410         // Simulate a submission.
411         $this->setUser($this->students[0]);
412         $submission = $assign->get_user_submission($this->students[0]->id, true);
413         $data = new stdClass();
414         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
415                                          'text'=>'Submission text',
416                                          'format'=>FORMAT_MOODLE);
417         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
418         $plugin->save($submission, $data);
420         $this->assertEquals(true, $assign->has_submissions_or_grades());
421         // Now try and reset.
422         $data = new stdClass();
423         $data->reset_assign_submissions = 1;
424         $data->reset_gradebook_grades = 1;
425         $data->courseid = $this->course->id;
426         $data->timeshift = 24*60*60;
427         $this->setUser($this->editingteachers[0]);
428         $assign->reset_userdata($data);
429         $this->assertEquals(false, $assign->has_submissions_or_grades());
431         // Reload the instance data.
432         $instance = $DB->get_record('assign', array('id'=>$assign->get_instance()->id));
433         $this->assertEquals($now + 24*60*60, $instance->duedate);
435         // Test reset using assign_reset_userdata().
436         $assignduedate = $instance->duedate; // Keep old updated value for comparison.
437         $data->timeshift = 2*24*60*60;
438         assign_reset_userdata($data);
439         $instance = $DB->get_record('assign', array('id' => $assign->get_instance()->id));
440         $this->assertEquals($assignduedate + 2*24*60*60, $instance->duedate);
442         // Create one more assignment and reset, make sure time shifted for previous assignment is not changed.
443         $assign2 = $this->create_instance(array('assignsubmission_onlinetext_enabled' => 1,
444                                                'duedate' => $now));
445         $assignduedate = $instance->duedate;
446         $data->timeshift = 3*24*60*60;
447         $assign2->reset_userdata($data);
448         $instance = $DB->get_record('assign', array('id' => $assign->get_instance()->id));
449         $this->assertEquals($assignduedate, $instance->duedate);
450         $instance2 = $DB->get_record('assign', array('id' => $assign2->get_instance()->id));
451         $this->assertEquals($now + 3*24*60*60, $instance2->duedate);
453         // Reset both assignments using assign_reset_userdata() and make sure both assignments have same date.
454         $assignduedate = $instance->duedate;
455         $assign2duedate = $instance2->duedate;
456         $data->timeshift = 4*24*60*60;
457         assign_reset_userdata($data);
458         $instance = $DB->get_record('assign', array('id' => $assign->get_instance()->id));
459         $this->assertEquals($assignduedate + 4*24*60*60, $instance->duedate);
460         $instance2 = $DB->get_record('assign', array('id' => $assign2->get_instance()->id));
461         $this->assertEquals($assign2duedate + 4*24*60*60, $instance2->duedate);
462     }
464     public function test_plugin_settings() {
465         global $DB;
467         $now = time();
468         $this->setUser($this->editingteachers[0]);
469         $assign = $this->create_instance(array('assignsubmission_file_enabled'=>1,
470                                                'assignsubmission_file_maxfiles'=>12,
471                                                'assignsubmission_file_maxsizebytes'=>10));
473         $plugin = $assign->get_submission_plugin_by_type('file');
474         $this->assertEquals('12', $plugin->get_config('maxfilesubmissions'));
475     }
477     public function test_update_calendar() {
478         global $DB;
480         $this->setUser($this->editingteachers[0]);
481         $userctx = context_user::instance($this->editingteachers[0]->id)->id;
483         // Hack to pretend that there was an editor involved. We need both $_POST and $_REQUEST, and a sesskey.
484         $draftid = file_get_unused_draft_itemid();
485         $_REQUEST['introeditor'] = $draftid;
486         $_POST['introeditor'] = $draftid;
487         $_POST['sesskey'] = sesskey();
489         // Write links to a draft area.
490         $fakearealink1 = file_rewrite_pluginfile_urls('<a href="@@PLUGINFILE@@/pic.gif">link</a>', 'draftfile.php', $userctx,
491             'user', 'draft', $draftid);
492         $fakearealink2 = file_rewrite_pluginfile_urls('<a href="@@PLUGINFILE@@/pic.gif">new</a>', 'draftfile.php', $userctx,
493             'user', 'draft', $draftid);
495         // Create a new assignment with links to a draft area.
496         $now = time();
497         $assign = $this->create_instance(array(
498             'duedate' => $now,
499             'intro' => $fakearealink1,
500             'introformat' => FORMAT_HTML
501         ));
503         // See if there is an event in the calendar.
504         $params = array('modulename'=>'assign', 'instance'=>$assign->get_instance()->id);
505         $event = $DB->get_record('event', $params);
506         $this->assertNotEmpty($event);
507         $this->assertSame('link', $event->description);     // The pluginfile links are removed.
509         // Make sure the same works when updating the assignment.
510         $instance = $assign->get_instance();
511         $instance->instance = $instance->id;
512         $instance->intro = $fakearealink2;
513         $instance->introformat = FORMAT_HTML;
514         $assign->update_instance($instance);
515         $params = array('modulename' => 'assign', 'instance' => $assign->get_instance()->id);
516         $event = $DB->get_record('event', $params);
517         $this->assertNotEmpty($event);
518         $this->assertSame('new', $event->description);     // The pluginfile links are removed.
520         // Create an assignment with a description that should be hidden.
521         $assign = $this->create_instance(array('duedate'=>$now + 160,
522                                                'alwaysshowdescription'=>false,
523                                                'allowsubmissionsfromdate'=>$now + 60,
524                                                'intro'=>'Some text'));
526         // Get the event from the calendar.
527         $params = array('modulename'=>'assign', 'instance'=>$assign->get_instance()->id);
528         $event = $DB->get_record('event', $params);
530         $this->assertEmpty($event->description);
532         // Change the allowsubmissionfromdate to the past - do this directly in the DB
533         // because if we call the assignment update method - it will update the calendar
534         // and we want to test that this works from cron.
535         $DB->set_field('assign', 'allowsubmissionsfromdate', $now - 60, array('id'=>$assign->get_instance()->id));
536         // Run cron to update the event in the calendar.
537         assign::cron();
538         $event = $DB->get_record('event', $params);
540         $this->assertContains('Some text', $event->description);
542     }
544     public function test_update_instance() {
545         global $DB;
547         $this->setUser($this->editingteachers[0]);
548         $assign = $this->create_instance(array('assignsubmission_onlinetext_enabled'=>1));
550         $now = time();
551         $instance = $assign->get_instance();
552         $instance->duedate = $now;
553         $instance->instance = $instance->id;
554         $instance->assignsubmission_onlinetext_enabled = 1;
556         $assign->update_instance($instance);
558         $instance = $DB->get_record('assign', array('id'=>$assign->get_instance()->id));
559         $this->assertEquals($now, $instance->duedate);
560     }
562     public function test_cannot_submit_empty() {
563         global $PAGE;
565         $this->setUser($this->editingteachers[0]);
566         $assign = $this->create_instance(array('submissiondrafts'=>1));
568         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
570         // Test you cannot see the submit button for an offline assignment regardless.
571         $this->setUser($this->students[0]);
572         $output = $assign->view_student_summary($this->students[0], true);
573         $this->assertNotContains(get_string('submitassignment', 'assign'), $output, 'Can submit empty offline assignment');
575         // Test you cannot see the submit button for an online text assignment with no submission.
576         $this->setUser($this->editingteachers[0]);
577         $instance = $assign->get_instance();
578         $instance->instance = $instance->id;
579         $instance->assignsubmission_onlinetext_enabled = 1;
581         $assign->update_instance($instance);
582         $this->setUser($this->students[0]);
583         $output = $assign->view_student_summary($this->students[0], true);
584         $this->assertNotContains(get_string('submitassignment', 'assign'), $output, 'Cannot submit empty onlinetext assignment');
586         // Simulate a submission.
587         $submission = $assign->get_user_submission($this->students[0]->id, true);
588         $data = new stdClass();
589         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
590                                          'text'=>'Submission text',
591                                          'format'=>FORMAT_MOODLE);
592         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
593         $plugin->save($submission, $data);
594         // Test you can see the submit button for an online text assignment with a submission.
595         $output = $assign->view_student_summary($this->students[0], true);
596         $this->assertContains(get_string('submitassignment', 'assign'), $output, 'Can submit non empty onlinetext assignment');
597     }
599     public function test_list_participants() {
600         global $CFG, $DB;
602         $this->create_extra_users();
603         $this->setUser($this->editingteachers[0]);
604         $assign = $this->create_instance(array('grade'=>100));
606         $this->assertEquals(self::DEFAULT_STUDENT_COUNT + self::EXTRA_STUDENT_COUNT, count($assign->list_participants(null, true)));
608         // Teacher with user preference set should see suspended users as well.
609         set_user_preference('grade_report_showonlyactiveenrol', false);
610         $assign = $this->create_instance(array('grade'=>100));
611         $this->assertEquals(self::DEFAULT_STUDENT_COUNT + self::EXTRA_STUDENT_COUNT + self::EXTRA_SUSPENDED_COUNT,
612                 count($assign->list_participants(null, true)));
614         // Non-editing teacher should not see suspended users, even if user preference is set.
615         $this->setUser($this->teachers[0]);
616         set_user_preference('grade_report_showonlyactiveenrol', false);
617         $assign = $this->create_instance(array('grade'=>100));
618         $this->assertEquals(self::DEFAULT_STUDENT_COUNT + self::EXTRA_STUDENT_COUNT, count($assign->list_participants(null, true)));
620         // Turn on availability and a group restriction, and check that it doesn't
621         // show users who aren't in the group.
622         $CFG->enableavailability = true;
623         $specialgroup = $this->getDataGenerator()->create_group(
624                 array('courseid' => $this->course->id));
625         $assign = $this->create_instance(array('grade' => 100,
626                 'availability' => json_encode(\core_availability\tree::get_root_json(
627                     array(\availability_group\condition::get_json($specialgroup->id))))));
628         groups_add_member($specialgroup, $this->students[0]);
629         groups_add_member($specialgroup, $this->students[1]);
630         $this->assertEquals(2, count($assign->list_participants(null, true)));
631     }
633     public function test_count_teams() {
634         $this->create_extra_users();
635         $this->setUser($this->editingteachers[0]);
636         $assign = $this->create_instance(array('teamsubmission'=>1));
638         $this->assertEquals(self::GROUP_COUNT + 1, $assign->count_teams());
639     }
641     public function test_submit_to_default_group() {
642         global $DB;
644         $this->preventResetByRollback();
645         $sink = $this->redirectMessages();
647         $this->setUser($this->editingteachers[0]);
648         $params = array('teamsubmission' => 1,
649                         'assignsubmission_onlinetext_enabled' => 1,
650                         'submissiondrafts'=>0);
651         $assign = $this->create_instance($params);
653         $newstudent = $this->getDataGenerator()->create_user();
654         $studentrole = $DB->get_record('role', array('shortname'=>'student'));
655         $this->getDataGenerator()->enrol_user($newstudent->id,
656                                               $this->course->id,
657                                               $studentrole->id);
658         $this->setUser($newstudent);
659         $data = new stdClass();
660         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
661                                          'text'=>'Submission text',
662                                          'format'=>FORMAT_MOODLE);
663         $notices = array();
665         $group = $assign->get_submission_group($newstudent->id);
666         $this->assertFalse($group, 'New student is in default group');
667         $assign->save_submission($data, $notices);
668         $this->assertEmpty($notices, 'No errors on save submission');
670         $sink->close();
671     }
673     public function test_count_submissions() {
674         $this->create_extra_users();
675         $this->setUser($this->editingteachers[0]);
676         $assign = $this->create_instance(array('assignsubmission_onlinetext_enabled'=>1));
678         // Simulate a submission.
679         $this->setUser($this->extrastudents[0]);
680         $submission = $assign->get_user_submission($this->extrastudents[0]->id, true);
681         $submission->status = ASSIGN_SUBMISSION_STATUS_DRAFT;
682         $assign->testable_update_submission($submission, $this->extrastudents[0]->id, true, false);
683         // Leave this one as DRAFT.
684         $data = new stdClass();
685         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
686                                          'text'=>'Submission text',
687                                          'format'=>FORMAT_MOODLE);
688         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
689         $plugin->save($submission, $data);
691         // Simulate adding a grade.
692         $this->setUser($this->teachers[0]);
693         $data = new stdClass();
694         $data->grade = '50.0';
695         $assign->testable_apply_grade_to_user($data, $this->extrastudents[0]->id, 0);
697         // Simulate a submission.
698         $this->setUser($this->extrastudents[1]);
699         $submission = $assign->get_user_submission($this->extrastudents[1]->id, true);
700         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
701         $assign->testable_update_submission($submission, $this->extrastudents[1]->id, true, false);
702         $data = new stdClass();
703         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
704                                          'text'=>'Submission text',
705                                          'format'=>FORMAT_MOODLE);
706         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
707         $plugin->save($submission, $data);
709         // Simulate a submission.
710         $this->setUser($this->extrastudents[2]);
711         $submission = $assign->get_user_submission($this->extrastudents[2]->id, true);
712         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
713         $assign->testable_update_submission($submission, $this->extrastudents[2]->id, true, false);
714         $data = new stdClass();
715         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
716                                          'text'=>'Submission text',
717                                          'format'=>FORMAT_MOODLE);
718         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
719         $plugin->save($submission, $data);
721         // Simulate a submission.
722         $this->setUser($this->extrastudents[3]);
723         $submission = $assign->get_user_submission($this->extrastudents[3]->id, true);
724         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
725         $assign->testable_update_submission($submission, $this->extrastudents[3]->id, true, false);
726         $data = new stdClass();
727         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
728                                          'text'=>'Submission text',
729                                          'format'=>FORMAT_MOODLE);
730         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
731         $plugin->save($submission, $data);
733         // Simulate a submission for suspended user, this will never be counted.
734         $this->setUser($this->extrastudents[3]);
735         $submission = $assign->get_user_submission($this->extrasuspendedstudents[0]->id, true);
736         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
737         $assign->testable_update_submission($submission, $this->extrasuspendedstudents[0]->id, true, false);
738         $data = new stdClass();
739         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
740                                          'text'=>'Submission text',
741                                          'format'=>FORMAT_MOODLE);
742         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
743         $plugin->save($submission, $data);
745         // Simulate adding a grade.
746         $this->setUser($this->teachers[0]);
747         $data = new stdClass();
748         $data->grade = '50.0';
749         $assign->testable_apply_grade_to_user($data, $this->extrastudents[3]->id, 0);
750         $assign->testable_apply_grade_to_user($data, $this->extrasuspendedstudents[0]->id, 0);
752         // Create a new submission with status NEW.
753         $this->setUser($this->extrastudents[4]);
754         $submission = $assign->get_user_submission($this->extrastudents[4]->id, true);
756         $this->assertEquals(2, $assign->count_grades());
757         $this->assertEquals(4, $assign->count_submissions());
758         $this->assertEquals(5, $assign->count_submissions(true));
759         $this->assertEquals(2, $assign->count_submissions_need_grading());
760         $this->assertEquals(3, $assign->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_SUBMITTED));
761         $this->assertEquals(1, $assign->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_DRAFT));
762     }
764     public function test_count_submissions_for_groups() {
765         $this->create_extra_users();
766         $groupid = null;
767         $this->setUser($this->editingteachers[0]);
768         $assign = $this->create_instance(array('assignsubmission_onlinetext_enabled' => 1, 'teamsubmission' => 1));
770         // Simulate a submission.
771         $this->setUser($this->extrastudents[0]);
772         $submission = $assign->get_group_submission($this->extrastudents[0]->id, $groupid, true);
773         $submission->status = ASSIGN_SUBMISSION_STATUS_DRAFT;
774         $assign->testable_update_submission($submission, $this->extrastudents[0]->id, true, false);
775         // Leave this one as DRAFT.
776         $data = new stdClass();
777         $data->onlinetext_editor = array('itemid' => file_get_unused_draft_itemid(),
778                                          'text' => 'Submission text',
779                                          'format' => FORMAT_MOODLE);
780         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
781         $plugin->save($submission, $data);
783         // Simulate adding a grade.
784         $this->setUser($this->teachers[0]);
785         $data = new stdClass();
786         $data->grade = '50.0';
787         $assign->testable_apply_grade_to_user($data, $this->extrastudents[0]->id, 0);
789         // Simulate a submission.
790         $this->setUser($this->extrastudents[1]);
791         $submission = $assign->get_group_submission($this->extrastudents[1]->id, $groupid, true);
792         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
793         $assign->testable_update_submission($submission, $this->extrastudents[1]->id, true, false);
794         $data = new stdClass();
795         $data->onlinetext_editor = array('itemid' => file_get_unused_draft_itemid(),
796                                          'text' => 'Submission text',
797                                          'format' => FORMAT_MOODLE);
798         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
799         $plugin->save($submission, $data);
801         // Simulate a submission.
802         $this->setUser($this->extrastudents[2]);
803         $submission = $assign->get_group_submission($this->extrastudents[2]->id, $groupid, true);
804         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
805         $assign->testable_update_submission($submission, $this->extrastudents[2]->id, true, false);
806         $data = new stdClass();
807         $data->onlinetext_editor = array('itemid' => file_get_unused_draft_itemid(),
808                                          'text' => 'Submission text',
809                                          'format' => FORMAT_MOODLE);
810         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
811         $plugin->save($submission, $data);
813         // Simulate a submission.
814         $this->setUser($this->extrastudents[3]);
815         $submission = $assign->get_group_submission($this->extrastudents[3]->id, $groupid, true);
816         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
817         $assign->testable_update_submission($submission, $this->extrastudents[3]->id, true, false);
818         $data = new stdClass();
819         $data->onlinetext_editor = array('itemid' => file_get_unused_draft_itemid(),
820                                          'text' => 'Submission text',
821                                          'format' => FORMAT_MOODLE);
822         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
823         $plugin->save($submission, $data);
825         // Simulate adding a grade.
826         $this->setUser($this->teachers[0]);
827         $data = new stdClass();
828         $data->grade = '50.0';
829         $assign->testable_apply_grade_to_user($data, $this->extrastudents[3]->id, 0);
830         $assign->testable_apply_grade_to_user($data, $this->extrasuspendedstudents[0]->id, 0);
832         // Create a new submission with status NEW.
833         $this->setUser($this->extrastudents[4]);
834         $submission = $assign->get_group_submission($this->extrastudents[4]->id, $groupid, true);
836         $this->assertEquals(2, $assign->count_grades());
837         $this->assertEquals(4, $assign->count_submissions());
838         $this->assertEquals(5, $assign->count_submissions(true));
839         $this->assertEquals(3, $assign->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_SUBMITTED));
840         $this->assertEquals(1, $assign->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_DRAFT));
841     }
843     public function test_get_grading_userid_list() {
844         $this->create_extra_users();
845         $this->setUser($this->editingteachers[0]);
846         $assign = $this->create_instance();
848         $users = $assign->testable_get_grading_userid_list();
849         $this->assertEquals(self::DEFAULT_STUDENT_COUNT + self::EXTRA_STUDENT_COUNT, count($users));
851         $this->setUser($this->editingteachers[0]);
852         set_user_preference('grade_report_showonlyactiveenrol', false);
853         $assign = $this->create_instance();
855         $users = $assign->testable_get_grading_userid_list();
856         $this->assertEquals(self::DEFAULT_STUDENT_COUNT + self::EXTRA_STUDENT_COUNT + self::EXTRA_SUSPENDED_COUNT, count($users));
857     }
859     public function test_cron() {
860         // First run cron so there are no messages waiting to be sent (from other tests).
861         cron_setup_user();
862         assign::cron();
864         // Now create an assignment and add some feedback.
865         $this->setUser($this->editingteachers[0]);
866         $assign = $this->create_instance(array('sendstudentnotifications'=>1));
868         // Simulate adding a grade.
869         $this->setUser($this->teachers[0]);
870         $data = new stdClass();
871         $data->grade = '50.0';
872         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
873         $assign->testable_apply_grade_to_user($data, $this->students[1]->id, 0);
875         $data->sendstudentnotifications = false;
876         $assign->testable_apply_grade_to_user($data, $this->students[2]->id, 0);
878         // Now run cron and see that one message was sent.
879         $this->preventResetByRollback();
880         $sink = $this->redirectMessages();
881         cron_setup_user();
882         $this->expectOutputRegex('/Done processing 2 assignment submissions/');
883         assign::cron();
885         $messages = $sink->get_messages();
886         // The sent count should be 2, because the 3rd one was marked as do not send notifications.
887         $this->assertEquals(2, count($messages));
888         $this->assertEquals(1, $messages[0]->notification);
889         $this->assertEquals($assign->get_instance()->name, $messages[0]->contexturlname);
890     }
892     public function test_is_graded() {
893         $this->setUser($this->editingteachers[0]);
894         $assign = $this->create_instance();
896         // Simulate adding a grade.
897         $this->setUser($this->teachers[0]);
898         $data = new stdClass();
899         $data->grade = '50.0';
900         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
902         $this->assertEquals(true, $assign->testable_is_graded($this->students[0]->id));
903         $this->assertEquals(false, $assign->testable_is_graded($this->students[1]->id));
904     }
906     public function test_can_grade() {
907         global $DB;
909         $this->setUser($this->editingteachers[0]);
910         $assign = $this->create_instance();
912         $this->setUser($this->students[0]);
913         $this->assertEquals(false, $assign->can_grade());
914         $this->setUser($this->editingteachers[0]);
915         $this->assertEquals(true, $assign->can_grade());
916         $this->setUser($this->teachers[0]);
917         $this->assertEquals(true, $assign->can_grade());
919         // Test the viewgrades capability - without mod/assign:grade.
920         $this->setUser($this->students[0]);
921         $studentrole = $DB->get_record('role', array('shortname' => 'student'));
922         assign_capability('mod/assign:viewgrades', CAP_ALLOW, $studentrole->id, $assign->get_context()->id);
923         $this->assertEquals(false, $assign->can_grade());
924     }
926     public function test_can_view_submission() {
927         global $DB;
929         $this->create_extra_users();
930         $this->setUser($this->editingteachers[0]);
931         $assign = $this->create_instance();
933         $this->setUser($this->students[0]);
934         $this->assertEquals(true, $assign->can_view_submission($this->students[0]->id));
935         $this->assertEquals(false, $assign->can_view_submission($this->students[1]->id));
936         $this->assertEquals(false, $assign->can_view_submission($this->teachers[0]->id));
937         $this->setUser($this->teachers[0]);
938         $this->assertEquals(true, $assign->can_view_submission($this->students[0]->id));
939         $this->assertEquals(true, $assign->can_view_submission($this->students[1]->id));
940         $this->assertEquals(true, $assign->can_view_submission($this->teachers[0]->id));
941         $this->assertEquals(false, $assign->can_view_submission($this->extrasuspendedstudents[0]->id));
942         $this->setUser($this->editingteachers[0]);
943         $this->assertEquals(true, $assign->can_view_submission($this->students[0]->id));
944         $this->assertEquals(true, $assign->can_view_submission($this->students[1]->id));
945         $this->assertEquals(true, $assign->can_view_submission($this->teachers[0]->id));
946         $this->assertEquals(true, $assign->can_view_submission($this->extrasuspendedstudents[0]->id));
948         // Test the viewgrades capability - without mod/assign:grade.
949         $this->setUser($this->students[0]);
950         $studentrole = $DB->get_record('role', array('shortname' => 'student'));
951         assign_capability('mod/assign:viewgrades', CAP_ALLOW, $studentrole->id, $assign->get_context()->id);
952         $this->assertEquals(true, $assign->can_view_submission($this->students[0]->id));
953         $this->assertEquals(true, $assign->can_view_submission($this->students[1]->id));
954         $this->assertEquals(true, $assign->can_view_submission($this->teachers[0]->id));
955         $this->assertEquals(false, $assign->can_view_submission($this->extrasuspendedstudents[0]->id));
956     }
959     public function test_update_submission() {
960         $this->create_extra_users();
961         $this->setUser($this->editingteachers[0]);
962         $assign = $this->create_instance();
964         $this->setUser($this->extrastudents[0]);
965         $now = time();
966         $submission = $assign->get_user_submission($this->extrastudents[0]->id, true);
967         $assign->testable_update_submission($submission, $this->extrastudents[0]->id, true, false);
969         $this->setUser($this->teachers[0]);
970         // Verify the gradebook update.
971         $gradinginfo = grade_get_grades($this->course->id,
972                                         'mod',
973                                         'assign',
974                                         $assign->get_instance()->id,
975                                         $this->extrastudents[0]->id);
977         $this->assertEquals($this->extrastudents[0]->id,
978                             $gradinginfo->items[0]->grades[$this->extrastudents[0]->id]->usermodified);
980         // Now verify group assignments.
981         $this->setUser($this->editingteachers[0]);
982         $assign = $this->create_instance(array('teamsubmission'=>1));
984         $this->setUser($this->extrastudents[0]);
985         $now = time();
986         $submission = $assign->get_group_submission($this->extrastudents[0]->id, 0, true);
987         $assign->testable_update_submission($submission, $this->extrastudents[0]->id, true, true);
989         // Check that at least 2 active members and 1 suspended member of the submission group had their submission updated.
991         $this->setUser($this->editingteachers[0]);
992         $gradinginfo = grade_get_grades($this->course->id,
993                                         'mod',
994                                         'assign',
995                                         $assign->get_instance()->id,
996                                         $this->extrastudents[0]->id);
998         $this->assertEquals($this->extrastudents[0]->id,
999                             $gradinginfo->items[0]->grades[$this->extrastudents[0]->id]->usermodified);
1001         $gradinginfo = grade_get_grades($this->course->id,
1002                                         'mod',
1003                                         'assign',
1004                                         $assign->get_instance()->id,
1005                                         $this->extrastudents[self::GROUP_COUNT]->id);
1007         $this->assertEquals($this->extrastudents[self::GROUP_COUNT]->id,
1008                             $gradinginfo->items[0]->grades[$this->extrastudents[self::GROUP_COUNT]->id]->usermodified);
1010         $gradinginfo = grade_get_grades($this->course->id,
1011                                         'mod',
1012                                         'assign',
1013                                         $assign->get_instance()->id,
1014                                         $this->extrasuspendedstudents[0]->id);
1015         $this->assertEquals($this->extrasuspendedstudents[0]->id,
1016                             $gradinginfo->items[0]->grades[$this->extrasuspendedstudents[0]->id]->usermodified);
1018         // Check the same with non-editing teacher and make sure submission is not updated for suspended user.
1019         $this->setUser($this->editingteachers[0]);
1020         $assign = $this->create_instance(array('teamsubmission'=>1));
1022         $this->setUser($this->extrastudents[1]);
1023         $now = time();
1024         $submission = $assign->get_group_submission($this->extrastudents[1]->id, 0, true);
1025         $assign->testable_update_submission($submission, $this->extrastudents[1]->id, true, true);
1027         $this->setUser($this->teachers[0]);
1028         $gradinginfo = grade_get_grades($this->course->id,
1029                                         'mod',
1030                                         'assign',
1031                                         $assign->get_instance()->id,
1032                                         $this->extrastudents[1]->id);
1034         $this->assertEquals($this->extrastudents[1]->id,
1035                             $gradinginfo->items[0]->grades[$this->extrastudents[1]->id]->usermodified);
1037         $gradinginfo = grade_get_grades($this->course->id,
1038                                         'mod',
1039                                         'assign',
1040                                         $assign->get_instance()->id,
1041                                         $this->extrastudents[self::GROUP_COUNT+1]->id);
1043         $this->assertEquals($this->extrastudents[self::GROUP_COUNT+1]->id,
1044                             $gradinginfo->items[0]->grades[$this->extrastudents[self::GROUP_COUNT+1]->id]->usermodified);
1046         $gradinginfo = grade_get_grades($this->course->id,
1047                                         'mod',
1048                                         'assign',
1049                                         $assign->get_instance()->id,
1050                                         $this->extrasuspendedstudents[1]->id);
1051         $this->assertEquals($this->extrasuspendedstudents[1]->id,
1052                             $gradinginfo->items[0]->grades[$this->extrasuspendedstudents[1]->id]->usermodified);
1054         // Now verify blind marking.
1055         $this->setUser($this->editingteachers[0]);
1056         $assign = $this->create_instance(array('blindmarking'=>1));
1058         $this->setUser($this->extrastudents[0]);
1059         $now = time();
1060         $submission = $assign->get_user_submission($this->extrastudents[0]->id, true);
1061         $assign->testable_update_submission($submission, $this->extrastudents[0]->id, true, false);
1063         $this->setUser($this->editingteachers[0]);
1064         $gradinginfo = grade_get_grades($this->course->id,
1065                                         'mod',
1066                                         'assign',
1067                                         $assign->get_instance()->id,
1068                                         $this->extrastudents[0]->id);
1070         $this->assertEquals(null, $gradinginfo->items[0]->grades[$this->extrastudents[0]->id]->datesubmitted);
1071     }
1073     public function test_group_submissions_submit_for_marking_requireallteammemberssubmit() {
1074         global $PAGE;
1076         $this->create_extra_users();
1077         // Now verify group assignments.
1078         $this->setUser($this->editingteachers[0]);
1079         $assign = $this->create_instance(array('teamsubmission'=>1,
1080                                                'assignsubmission_onlinetext_enabled'=>1,
1081                                                'submissiondrafts'=>1,
1082                                                'requireallteammemberssubmit'=>1));
1083         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
1085         // Add a submission.
1086         $this->setUser($this->extrastudents[0]);
1087         $data = new stdClass();
1088         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
1089                                          'text'=>'Submission text',
1090                                          'format'=>FORMAT_MOODLE);
1092         $notices = array();
1093         $assign->save_submission($data, $notices);
1095         // Check we can see the submit button.
1096         $output = $assign->view_student_summary($this->extrastudents[0], true);
1097         $this->assertContains(get_string('submitassignment', 'assign'), $output);
1099         $submission = $assign->get_group_submission($this->extrastudents[0]->id, 0, true);
1100         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
1101         $assign->testable_update_submission($submission, $this->extrastudents[0]->id, true, true);
1103         // Check that the student does not see "Submit" button.
1104         $output = $assign->view_student_summary($this->extrastudents[0], true);
1105         $this->assertNotContains(get_string('submitassignment', 'assign'), $output);
1107         // Change to another user in the same group.
1108         $this->setUser($this->extrastudents[self::GROUP_COUNT]);
1109         $output = $assign->view_student_summary($this->extrastudents[self::GROUP_COUNT], true);
1110         $this->assertContains(get_string('submitassignment', 'assign'), $output);
1112         $submission = $assign->get_group_submission($this->extrastudents[self::GROUP_COUNT]->id, 0, true);
1113         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
1114         $assign->testable_update_submission($submission, $this->extrastudents[self::GROUP_COUNT]->id, true, true);
1115         $output = $assign->view_student_summary($this->extrastudents[self::GROUP_COUNT], true);
1116         $this->assertNotContains(get_string('submitassignment', 'assign'), $output);
1117     }
1119     public function test_group_submissions_submit_for_marking() {
1120         global $PAGE;
1122         $this->create_extra_users();
1123         // Now verify group assignments.
1124         $this->setUser($this->editingteachers[0]);
1125         $time = time();
1126         $assign = $this->create_instance(array('teamsubmission'=>1,
1127                                                'assignsubmission_onlinetext_enabled'=>1,
1128                                                'submissiondrafts'=>1,
1129                                                'requireallteammemberssubmit'=>0,
1130                                                'duedate' => $time - 2*24*60*60));
1131         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
1133         $this->setUser($this->extrastudents[0]);
1134         // Add a submission.
1135         $data = new stdClass();
1136         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
1137                                          'text'=>'Submission text',
1138                                          'format'=>FORMAT_MOODLE);
1140         $notices = array();
1141         $assign->save_submission($data, $notices);
1143         // Check we can see the submit button.
1144         $output = $assign->view_student_summary($this->extrastudents[0], true);
1145         $this->assertContains(get_string('submitassignment', 'assign'), $output);
1146         $this->assertContains(get_string('timeremaining', 'assign'), $output);
1147         $difftime = time() - $time;
1148         $this->assertContains(get_string('overdue', 'assign', format_time(2*24*60*60 + $difftime)), $output);
1150         $submission = $assign->get_group_submission($this->extrastudents[0]->id, 0, true);
1151         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
1152         $assign->testable_update_submission($submission, $this->extrastudents[0]->id, true, true);
1154         // Check that the student does not see "Submit" button.
1155         $output = $assign->view_student_summary($this->extrastudents[0], true);
1156         $this->assertNotContains(get_string('submitassignment', 'assign'), $output);
1158         // Change to another user in the same group.
1159         $this->setUser($this->extrastudents[self::GROUP_COUNT]);
1160         $output = $assign->view_student_summary($this->extrastudents[self::GROUP_COUNT], true);
1161         $this->assertNotContains(get_string('submitassignment', 'assign'), $output);
1163         // Check that time remaining is not overdue.
1164         $this->assertContains(get_string('timeremaining', 'assign'), $output);
1165         $difftime = time() - $time;
1166         $this->assertContains(get_string('submittedlate', 'assign', format_time(2*24*60*60 + $difftime)), $output);
1168         $submission = $assign->get_group_submission($this->extrastudents[self::GROUP_COUNT]->id, 0, true);
1169         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
1170         $assign->testable_update_submission($submission, $this->extrastudents[self::GROUP_COUNT]->id, true, true);
1171         $output = $assign->view_student_summary($this->extrastudents[self::GROUP_COUNT], true);
1172         $this->assertNotContains(get_string('submitassignment', 'assign'), $output);
1173     }
1175     public function test_submissions_open() {
1176         $this->setUser($this->editingteachers[0]);
1178         $now = time();
1179         $tomorrow = $now + 24*60*60;
1180         $oneweek = $now + 7*24*60*60;
1181         $yesterday = $now - 24*60*60;
1183         $assign = $this->create_instance();
1184         $this->assertEquals(true, $assign->testable_submissions_open($this->students[0]->id));
1186         $assign = $this->create_instance(array('duedate'=>$tomorrow));
1187         $this->assertEquals(true, $assign->testable_submissions_open($this->students[0]->id));
1189         $assign = $this->create_instance(array('duedate'=>$yesterday));
1190         $this->assertEquals(true, $assign->testable_submissions_open($this->students[0]->id));
1192         $assign = $this->create_instance(array('duedate'=>$yesterday, 'cutoffdate'=>$tomorrow));
1193         $this->assertEquals(true, $assign->testable_submissions_open($this->students[0]->id));
1195         $assign = $this->create_instance(array('duedate'=>$yesterday, 'cutoffdate'=>$yesterday));
1196         $this->assertEquals(false, $assign->testable_submissions_open($this->students[0]->id));
1198         $assign->testable_save_user_extension($this->students[0]->id, $tomorrow);
1199         $this->assertEquals(true, $assign->testable_submissions_open($this->students[0]->id));
1201         $assign = $this->create_instance(array('submissiondrafts'=>1));
1202         $this->assertEquals(true, $assign->testable_submissions_open($this->students[0]->id));
1204         $this->setUser($this->students[0]);
1205         $now = time();
1206         $submission = $assign->get_user_submission($this->students[0]->id, true);
1207         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
1208         $assign->testable_update_submission($submission, $this->students[0]->id, true, false);
1209         $this->setUser($this->editingteachers[0]);
1210         $this->assertEquals(false, $assign->testable_submissions_open($this->students[0]->id));
1211     }
1213     public function test_get_graders() {
1214         $this->create_extra_users();
1215         $this->setUser($this->editingteachers[0]);
1217         // Create an assignment with no groups.
1218         $assign = $this->create_instance();
1219         $this->assertCount(self::DEFAULT_TEACHER_COUNT +
1220                            self::DEFAULT_EDITING_TEACHER_COUNT +
1221                            self::EXTRA_TEACHER_COUNT +
1222                            self::EXTRA_EDITING_TEACHER_COUNT,
1223                            $assign->testable_get_graders($this->students[0]->id));
1225         // Force create an assignment with SEPARATEGROUPS.
1226         $data = new stdClass();
1227         $data->courseid = $this->course->id;
1228         $data->name = 'Grouping';
1229         $groupingid = groups_create_grouping($data);
1230         groups_assign_grouping($groupingid, $this->groups[0]->id);
1231         $assign = $this->create_instance(array('groupingid' => $groupingid, 'groupmode' => SEPARATEGROUPS));
1233         $this->setUser($this->students[1]);
1234         $this->assertCount(4, $assign->testable_get_graders($this->students[0]->id));
1235         // Note the second student is in a group that is not in the grouping.
1236         // This means that we get all graders that are not in a group in the grouping.
1237         $this->assertCount(10, $assign->testable_get_graders($this->students[1]->id));
1238     }
1240     public function test_get_notified_users() {
1241         global $CFG, $DB;
1243         $capability = 'mod/assign:receivegradernotifications';
1244         $coursecontext = context_course::instance($this->course->id);
1245         $role = $DB->get_record('role', array('shortname' => 'teacher'));
1247         $this->create_extra_users();
1248         $this->setUser($this->editingteachers[0]);
1250         // Create an assignment with no groups.
1251         $assign = $this->create_instance();
1253         $this->assertCount(self::DEFAULT_TEACHER_COUNT +
1254                            self::DEFAULT_EDITING_TEACHER_COUNT +
1255                            self::EXTRA_TEACHER_COUNT +
1256                            self::EXTRA_EDITING_TEACHER_COUNT,
1257                            $assign->testable_get_notifiable_users($this->students[0]->id));
1259         // Change nonediting teachers role to not receive grader notifications.
1260         assign_capability($capability, CAP_PROHIBIT, $role->id, $coursecontext);
1262         $this->assertCount(self::DEFAULT_EDITING_TEACHER_COUNT +
1263                            self::EXTRA_EDITING_TEACHER_COUNT,
1264                            $assign->testable_get_notifiable_users($this->students[0]->id));
1266         // Reset nonediting teachers role to default.
1267         unassign_capability($capability, $role->id, $coursecontext);
1269         // Force create an assignment with SEPARATEGROUPS.
1270         $data = new stdClass();
1271         $data->courseid = $this->course->id;
1272         $data->name = 'Grouping';
1273         $groupingid = groups_create_grouping($data);
1274         groups_assign_grouping($groupingid, $this->groups[0]->id);
1275         $assign = $this->create_instance(array('groupingid' => $groupingid, 'groupmode' => SEPARATEGROUPS));
1277         $this->setUser($this->students[1]);
1278         $this->assertCount(4, $assign->testable_get_notifiable_users($this->students[0]->id));
1279         // Note the second student is in a group that is not in the grouping.
1280         // This means that we get all graders that are not in a group in the grouping.
1281         $this->assertCount(10, $assign->testable_get_notifiable_users($this->students[1]->id));
1283         // Change nonediting teachers role to not receive grader notifications.
1284         assign_capability($capability, CAP_PROHIBIT, $role->id, $coursecontext);
1286         $this->assertCount(2, $assign->testable_get_notifiable_users($this->students[0]->id));
1287         // Note the second student is in a group that is not in the grouping.
1288         // This means that we get all graders that are not in a group in the grouping.
1289         $this->assertCount(5, $assign->testable_get_notifiable_users($this->students[1]->id));
1290     }
1292     public function test_group_members_only() {
1293         global $CFG;
1295         $this->setAdminUser();
1296         $this->create_extra_users();
1297         $CFG->enableavailability = true;
1298         $grouping = $this->getDataGenerator()->create_grouping(array('courseid' => $this->course->id));
1299         groups_assign_grouping($grouping->id, $this->groups[0]->id);
1301         // Force create an assignment with SEPARATEGROUPS.
1302         $instance = $this->getDataGenerator()->create_module('assign', array('course'=>$this->course->id),
1303                 array('availability' => json_encode(\core_availability\tree::get_root_json(array(
1304                     \availability_grouping\condition::get_json()))),
1305                 'groupingid' => $grouping->id));
1307         $cm = get_coursemodule_from_instance('assign', $instance->id);
1308         $context = context_module::instance($cm->id);
1309         $assign = new testable_assign($context, $cm, $this->course);
1311         $this->setUser($this->teachers[0]);
1312         get_fast_modinfo($this->course, 0, true);
1313         $this->assertCount(5, $assign->list_participants(0, true));
1315     }
1317     public function test_get_uniqueid_for_user() {
1318         $this->setUser($this->editingteachers[0]);
1319         $assign = $this->create_instance();
1321         foreach ($this->students as $student) {
1322             $uniqueid = $assign->get_uniqueid_for_user($student->id);
1323             $this->assertEquals($student->id, $assign->get_user_id_for_uniqueid($uniqueid));
1324         }
1325     }
1327     public function test_show_student_summary() {
1328         global $CFG, $PAGE;
1330         $this->setUser($this->editingteachers[0]);
1331         $assign = $this->create_instance();
1332         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
1334         // No feedback should be available because this student has not been graded.
1335         $this->setUser($this->students[0]);
1336         $output = $assign->view_student_summary($this->students[0], true);
1337         $this->assertEquals(false, strpos($output, 'Feedback'), 'Do not show feedback if there is no grade');
1338         // Simulate adding a grade.
1339         $this->setUser($this->teachers[0]);
1340         $data = new stdClass();
1341         $data->grade = '50.0';
1342         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
1344         // Now we should see the feedback.
1345         $this->setUser($this->students[0]);
1346         $output = $assign->view_student_summary($this->students[0], true);
1347         $this->assertNotEquals(false, strpos($output, 'Feedback'), 'Show feedback if there is a grade');
1349         // Now hide the grade in gradebook.
1350         $this->setUser($this->teachers[0]);
1351         require_once($CFG->libdir.'/gradelib.php');
1352         $gradeitem = new grade_item(array(
1353             'itemtype'      => 'mod',
1354             'itemmodule'    => 'assign',
1355             'iteminstance'  => $assign->get_instance()->id,
1356             'courseid'      => $this->course->id));
1358         $gradeitem->set_hidden(1, false);
1360         // No feedback should be available because the grade is hidden.
1361         $this->setUser($this->students[0]);
1362         $output = $assign->view_student_summary($this->students[0], true);
1363         $this->assertEquals(false, strpos($output, 'Feedback'), 'Do not show feedback if the grade is hidden in the gradebook');
1365         // Do the same but add feedback.
1366         $assign = $this->create_instance(array('assignfeedback_comments_enabled' => 1));
1368         $this->setUser($this->teachers[0]);
1369         $grade = $assign->get_user_grade($this->students[0]->id, true);
1370         $data = new stdClass();
1371         $data->assignfeedbackcomments_editor = array('text'=>'Tomato sauce',
1372                                          'format'=>FORMAT_MOODLE);
1373         $plugin = $assign->get_feedback_plugin_by_type('comments');
1374         $plugin->save($grade, $data);
1376         // Should have feedback but no grade.
1377         $this->setUser($this->students[0]);
1378         $output = $assign->view_student_summary($this->students[0], true);
1379         $this->assertNotEquals(false, strpos($output, 'Feedback'), 'Show feedback even if there is no grade');
1380         $this->assertEquals(false, strpos($output, 'Grade'), 'Do not show grade when there is no grade.');
1381         $this->assertEquals(false, strpos($output, 'Graded on'), 'Do not show graded date when there is no grade.');
1383         // Now hide the grade in gradebook.
1384         $this->setUser($this->teachers[0]);
1385         $gradeitem = new grade_item(array(
1386             'itemtype'      => 'mod',
1387             'itemmodule'    => 'assign',
1388             'iteminstance'  => $assign->get_instance()->id,
1389             'courseid'      => $this->course->id));
1391         $gradeitem->set_hidden(1, false);
1393         // No feedback should be available because the grade is hidden.
1394         $this->setUser($this->students[0]);
1395         $output = $assign->view_student_summary($this->students[0], true);
1396         $this->assertEquals(false, strpos($output, 'Feedback'), 'Do not show feedback if the grade is hidden in the gradebook');
1397     }
1399     public function test_attempt_reopen_method_manual() {
1400         global $PAGE;
1402         $this->setUser($this->editingteachers[0]);
1403         $assign = $this->create_instance(array('attemptreopenmethod'=>ASSIGN_ATTEMPT_REOPEN_METHOD_MANUAL,
1404                                                'maxattempts'=>3,
1405                                                'submissiondrafts'=>1,
1406                                                'assignsubmission_onlinetext_enabled'=>1));
1407         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
1409         // Student should be able to see an add submission button.
1410         $this->setUser($this->students[0]);
1411         $output = $assign->view_student_summary($this->students[0], true);
1412         $this->assertNotEquals(false, strpos($output, get_string('addsubmission', 'assign')));
1414         // Add a submission.
1415         $now = time();
1416         $submission = $assign->get_user_submission($this->students[0]->id, true);
1417         $data = new stdClass();
1418         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
1419                                          'text'=>'Submission text',
1420                                          'format'=>FORMAT_MOODLE);
1421         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
1422         $plugin->save($submission, $data);
1424         // And now submit it for marking.
1425         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
1426         $assign->testable_update_submission($submission, $this->students[0]->id, true, false);
1428         // Verify the student cannot make changes to the submission.
1429         $output = $assign->view_student_summary($this->students[0], true);
1430         $this->assertEquals(false, strpos($output, get_string('addsubmission', 'assign')));
1432         // Mark the submission.
1433         $this->setUser($this->teachers[0]);
1434         $data = new stdClass();
1435         $data->grade = '50.0';
1436         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
1438         // Check the student can see the grade.
1439         $this->setUser($this->students[0]);
1440         $output = $assign->view_student_summary($this->students[0], true);
1441         $this->assertNotEquals(false, strpos($output, '50.0'));
1443         // Allow the student another attempt.
1444         $this->teachers[0]->ignoresesskey = true;
1445         $this->setUser($this->teachers[0]);
1446         $result = $assign->testable_process_add_attempt($this->students[0]->id);
1447         $this->assertEquals(true, $result);
1449         // Check that the previous attempt is now in the submission history table.
1450         $this->setUser($this->students[0]);
1451         $output = $assign->view_student_summary($this->students[0], true);
1452         // Need a better check.
1453         $this->assertNotEquals(false, strpos($output, 'Submission text'), 'Contains: Submission text');
1455         // Check that the student now has a button for Add a new attempt".
1456         $this->assertNotEquals(false, strpos($output, get_string('addnewattempt', 'assign')));
1457         // Check that the student now does not have a button for Submit.
1458         $this->assertEquals(false, strpos($output, get_string('submitassignment', 'assign')));
1460         // Check that the student now has a submission history.
1461         $this->assertNotEquals(false, strpos($output, get_string('attempthistory', 'assign')));
1463         $this->setUser($this->teachers[0]);
1464         // Check that the grading table loads correctly and contains this user.
1465         // This is also testing that we do not get duplicate rows in the grading table.
1466         $gradingtable = new assign_grading_table($assign, 100, '', 0, true);
1467         $output = $assign->get_renderer()->render($gradingtable);
1468         $this->assertEquals(true, strpos($output, $this->students[0]->lastname));
1470         // Should be 1 not 2.
1471         $this->assertEquals(1, $assign->count_submissions());
1472         $this->assertEquals(1, $assign->count_submissions_with_status('reopened'));
1473         $this->assertEquals(0, $assign->count_submissions_need_grading());
1474         $this->assertEquals(1, $assign->count_grades());
1476         // Change max attempts to unlimited.
1477         $formdata = clone($assign->get_instance());
1478         $formdata->maxattempts = ASSIGN_UNLIMITED_ATTEMPTS;
1479         $formdata->instance = $formdata->id;
1480         $assign->update_instance($formdata);
1482         // Mark the submission again.
1483         $data = new stdClass();
1484         $data->grade = '60.0';
1485         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 1);
1487         // Check the grade exists.
1488         $grades = $assign->get_user_grades_for_gradebook($this->students[0]->id);
1489         $this->assertEquals(60, (int)$grades[$this->students[0]->id]->rawgrade);
1491         // Check we can reopen still.
1492         $result = $assign->testable_process_add_attempt($this->students[0]->id);
1493         $this->assertEquals(true, $result);
1495         // Should no longer have a grade because there is no grade for the latest attempt.
1496         $grades = $assign->get_user_grades_for_gradebook($this->students[0]->id);
1497         $this->assertEmpty($grades);
1499     }
1501     public function test_markingworkflow() {
1502         global $PAGE;
1504         $this->setUser($this->editingteachers[0]);
1505         $assign = $this->create_instance(array('markingworkflow'=>1));
1506         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
1508         // Mark the submission and set to notmarked.
1509         $this->setUser($this->teachers[0]);
1510         $data = new stdClass();
1511         $data->grade = '50.0';
1512         $data->workflowstate = ASSIGN_MARKING_WORKFLOW_STATE_NOTMARKED;
1513         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
1515         // Check the student can't see the grade.
1516         $this->setUser($this->students[0]);
1517         $output = $assign->view_student_summary($this->students[0], true);
1518         $this->assertEquals(false, strpos($output, '50.0'));
1520         // Mark the submission and set to inmarking.
1521         $this->setUser($this->teachers[0]);
1522         $data = new stdClass();
1523         $data->grade = '50.0';
1524         $data->workflowstate = ASSIGN_MARKING_WORKFLOW_STATE_INMARKING;
1525         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
1527         // Check the student can't see the grade.
1528         $this->setUser($this->students[0]);
1529         $output = $assign->view_student_summary($this->students[0], true);
1530         $this->assertEquals(false, strpos($output, '50.0'));
1532         // Mark the submission and set to readyforreview.
1533         $this->setUser($this->teachers[0]);
1534         $data = new stdClass();
1535         $data->grade = '50.0';
1536         $data->workflowstate = ASSIGN_MARKING_WORKFLOW_STATE_READYFORREVIEW;
1537         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
1539         // Check the student can't see the grade.
1540         $this->setUser($this->students[0]);
1541         $output = $assign->view_student_summary($this->students[0], true);
1542         $this->assertEquals(false, strpos($output, '50.0'));
1544         // Mark the submission and set to inreview.
1545         $this->setUser($this->teachers[0]);
1546         $data = new stdClass();
1547         $data->grade = '50.0';
1548         $data->workflowstate = ASSIGN_MARKING_WORKFLOW_STATE_INREVIEW;
1549         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
1551         // Check the student can't see the grade.
1552         $this->setUser($this->students[0]);
1553         $output = $assign->view_student_summary($this->students[0], true);
1554         $this->assertEquals(false, strpos($output, '50.0'));
1556         // Mark the submission and set to readyforrelease.
1557         $this->setUser($this->teachers[0]);
1558         $data = new stdClass();
1559         $data->grade = '50.0';
1560         $data->workflowstate = ASSIGN_MARKING_WORKFLOW_STATE_READYFORRELEASE;
1561         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
1563         // Check the student can't see the grade.
1564         $this->setUser($this->students[0]);
1565         $output = $assign->view_student_summary($this->students[0], true);
1566         $this->assertEquals(false, strpos($output, '50.0'));
1568         // Mark the submission and set to released.
1569         $this->setUser($this->teachers[0]);
1570         $data = new stdClass();
1571         $data->grade = '50.0';
1572         $data->workflowstate = ASSIGN_MARKING_WORKFLOW_STATE_RELEASED;
1573         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
1575         // Check the student can see the grade.
1576         $this->setUser($this->students[0]);
1577         $output = $assign->view_student_summary($this->students[0], true);
1578         $this->assertNotEquals(false, strpos($output, '50.0'));
1579     }
1581     public function test_markerallocation() {
1582         global $PAGE;
1584         $this->setUser($this->editingteachers[0]);
1585         $assign = $this->create_instance(array('markingworkflow'=>1, 'markingallocation'=>1));
1586         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
1588         // Allocate marker to submission.
1589         $data = new stdClass();
1590         $data->allocatedmarker = $this->teachers[0]->id;
1591         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
1593         // Check the allocated marker can view the submission.
1594         $this->setUser($this->teachers[0]);
1595         $gradingtable = new assign_grading_table($assign, 100, '', 0, true);
1596         $output = $assign->get_renderer()->render($gradingtable);
1597         $this->assertEquals(true, strpos($output, $this->students[0]->lastname));
1599         // Check that other teachers can't view this submission.
1600         $this->setUser($this->teachers[1]);
1601         $gradingtable = new assign_grading_table($assign, 100, '', 0, true);
1602         $output = $assign->get_renderer()->render($gradingtable);
1603         $this->assertNotEquals(true, strpos($output, $this->students[0]->lastname));
1604     }
1608     public function test_teacher_submit_for_student() {
1609         global $PAGE;
1611         $this->preventResetByRollback();
1612         $sink = $this->redirectMessages();
1614         $this->setUser($this->editingteachers[0]);
1616         $assign = $this->create_instance(array('assignsubmission_onlinetext_enabled'=>1, 'submissiondrafts'=>1));
1617         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
1619         $this->setUser($this->students[0]);
1620         // Simulate a submission.
1621         $data = new stdClass();
1622         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
1623                                          'text'=>'Student submission text',
1624                                          'format'=>FORMAT_MOODLE);
1626         $notices = array();
1627         $assign->save_submission($data, $notices);
1629         // Check that the submission text was saved.
1630         $output = $assign->view_student_summary($this->students[0], true);
1631         $this->assertContains('Student submission text', $output, 'Contains student submission text');
1633         // Check that a teacher teacher with the extra capability can edit a students submission.
1634         $this->setUser($this->teachers[0]);
1635         $data = new stdClass();
1636         $data->userid = $this->students[0]->id;
1637         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
1638                                          'text'=>'Teacher edited submission text',
1639                                          'format'=>FORMAT_MOODLE);
1641         // Add the required capability.
1642         $roleid = create_role('Dummy role', 'dummyrole', 'dummy role description');
1643         assign_capability('mod/assign:editothersubmission', CAP_ALLOW, $roleid, $assign->get_context()->id);
1644         role_assign($roleid, $this->teachers[0]->id, $assign->get_context()->id);
1645         accesslib_clear_all_caches_for_unit_testing();
1647         // Try to save the submission.
1648         $notices = array();
1649         $assign->save_submission($data, $notices);
1651         // Check that the teacher can submit the students work.
1652         $data = new stdClass();
1653         $data->userid = $this->students[0]->id;
1654         $notices = array();
1655         $assign->submit_for_grading($data, $notices);
1657         // Revert to draft so the student can edit it.
1658         $assign->revert_to_draft($this->students[0]->id);
1660         $this->setUser($this->students[0]);
1662         // Check that the submission text was saved.
1663         $output = $assign->view_student_summary($this->students[0], true);
1664         $this->assertContains('Teacher edited submission text', $output, 'Contains student submission text');
1666         // Check that the student can submit their work.
1667         $data = new stdClass();
1668         $assign->submit_for_grading($data, $notices);
1670         $output = $assign->view_student_summary($this->students[0], true);
1671         $this->assertNotContains(get_string('addsubmission', 'assign'), $output);
1673         // Set to a default editing teacher who should not be able to edit this submission.
1674         $this->setUser($this->editingteachers[1]);
1676         // Revert to draft so the submission is editable.
1677         $assign->revert_to_draft($this->students[0]->id);
1679         $data = new stdClass();
1680         $data->userid = $this->students[0]->id;
1681         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
1682                                          'text'=>'Teacher 2 edited submission text',
1683                                          'format'=>FORMAT_MOODLE);
1685         $notices = array();
1686         $this->setExpectedException('moodle_exception');
1687         $assign->save_submission($data, $notices);
1689         $sink->close();
1690     }
1692     public function test_disable_submit_after_cutoff_date() {
1693         global $PAGE;
1695         $this->setUser($this->editingteachers[0]);
1696         $now = time();
1697         $tomorrow = $now + 24*60*60;
1698         $lastweek = $now - 7*24*60*60;
1699         $yesterday = $now - 24*60*60;
1701         $assign = $this->create_instance(array('duedate'=>$yesterday,
1702                                                'cutoffdate'=>$tomorrow,
1703                                                'assignsubmission_onlinetext_enabled'=>1));
1704         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
1706         // Student should be able to see an add submission button.
1707         $this->setUser($this->students[0]);
1708         $output = $assign->view_student_summary($this->students[0], true);
1709         $this->assertNotEquals(false, strpos($output, get_string('addsubmission', 'assign')));
1711         // Add a submission but don't submit now.
1712         $submission = $assign->get_user_submission($this->students[0]->id, true);
1713         $data = new stdClass();
1714         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
1715                                          'text'=>'Submission text',
1716                                          'format'=>FORMAT_MOODLE);
1717         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
1718         $plugin->save($submission, $data);
1720         // Create another instance with cut-off and due-date already passed.
1721         $this->setUser($this->editingteachers[0]);
1722         $now = time();
1723         $assign = $this->create_instance(array('duedate'=>$lastweek,
1724                                                'cutoffdate'=>$yesterday,
1725                                                'assignsubmission_onlinetext_enabled'=>1));
1727         $this->setUser($this->students[0]);
1728         $output = $assign->view_student_summary($this->students[0], true);
1729         $this->assertNotContains($output, get_string('editsubmission', 'assign'),
1730                                  'Should not be able to edit after cutoff date.');
1731         $this->assertNotContains($output, get_string('submitassignment', 'assign'),
1732                                  'Should not be able to submit after cutoff date.');
1733     }
1734     /**
1735      * Testing for submission comment plugin settings
1736      */
1737     public function test_submission_comment_plugin_settings() {
1738         global $CFG;
1740         $commentconfig = false;
1741         if (!empty($CFG->usecomments)) {
1742             $commentconfig = $CFG->usecomments;
1743         }
1745         $CFG->usecomments = true;
1746         $assign = $this->create_instance();
1747         $plugin = $assign->get_submission_plugin_by_type('comments');
1748         $this->assertEquals(1, $plugin->is_enabled('enabled'));
1750         $assign = $this->create_instance(array('assignsubmission_comments_enabled' => 0));
1751         $plugin = $assign->get_submission_plugin_by_type('comments');
1752         $this->assertEquals(1, $plugin->is_enabled('enabled'));
1754         $assign = $this->create_instance(array('assignsubmission_comments_enabled' => 1));
1755         $plugin = $assign->get_submission_plugin_by_type('comments');
1756         $this->assertEquals(1, $plugin->is_enabled('enabled'));
1758         $CFG->usecomments = false;
1759         $assign = $this->create_instance();
1760         $plugin = $assign->get_submission_plugin_by_type('comments');
1761         $this->assertEquals(0, $plugin->is_enabled('enabled'));
1763         $assign = $this->create_instance(array('assignsubmission_comments_enabled' => 0));
1764         $plugin = $assign->get_submission_plugin_by_type('comments');
1765         $this->assertEquals(0, $plugin->is_enabled('enabled'));
1767         $assign = $this->create_instance(array('assignsubmission_comments_enabled' => 1));
1768         $plugin = $assign->get_submission_plugin_by_type('comments');
1769         $this->assertEquals(0, $plugin->is_enabled('enabled'));
1771         $CFG->usecomments = $commentconfig;
1772     }
1774     /**
1775      * Testing for comment inline settings
1776      */
1777     public function test_feedback_comment_commentinline() {
1778         global $CFG;
1780         $sourcetext = "Hello!
1782 I'm writing to you from the Moodle Majlis in Muscat, Oman, where we just had several days of Moodle community goodness.
1784 URL outside a tag: https://moodle.org/logo/logo-240x60.gif
1785 Plugin url outside a tag: @@PLUGINFILE@@/logo-240x60.gif
1787 External link 1:<img src='https://moodle.org/logo/logo-240x60.gif' alt='Moodle'/>
1788 External link 2:<img alt=\"Moodle\" src=\"https://moodle.org/logo/logo-240x60.gif\"/>
1789 Internal link 1:<img src='@@PLUGINFILE@@/logo-240x60.gif' alt='Moodle'/>
1790 Internal link 2:<img alt=\"Moodle\" src=\"@@PLUGINFILE@@logo-240x60.gif\"/>
1791 Anchor link 1:<a href=\"@@PLUGINFILE@@logo-240x60.gif\" alt=\"bananas\">Link text</a>
1792 Anchor link 2:<a title=\"bananas\" href=\"../logo-240x60.gif\">Link text</a>
1793 ";
1795         // Note the internal images have been stripped and the html is purified (quotes fixed in this case).
1796         $filteredtext = "Hello!
1798 I'm writing to you from the Moodle Majlis in Muscat, Oman, where we just had several days of Moodle community goodness.
1800 URL outside a tag: https://moodle.org/logo/logo-240x60.gif
1801 Plugin url outside a tag: @@PLUGINFILE@@/logo-240x60.gif
1803 External link 1:<img src=\"https://moodle.org/logo/logo-240x60.gif\" alt=\"Moodle\" />
1804 External link 2:<img alt=\"Moodle\" src=\"https://moodle.org/logo/logo-240x60.gif\" />
1805 Internal link 1:
1806 Internal link 2:
1807 Anchor link 1:Link text
1808 Anchor link 2:<a title=\"bananas\" href=\"../logo-240x60.gif\">Link text</a>
1809 ";
1811         $this->setUser($this->editingteachers[0]);
1812         $params = array('assignsubmission_onlinetext_enabled' => 1,
1813                         'assignfeedback_comments_enabled' => 1,
1814                         'assignfeedback_comments_commentinline' => 1);
1815         $assign = $this->create_instance($params);
1817         $this->setUser($this->students[0]);
1818         // Add a submission but don't submit now.
1819         $submission = $assign->get_user_submission($this->students[0]->id, true);
1820         $data = new stdClass();
1822         // Test the internal link is stripped, but the external one is not.
1823         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
1824                                          'text'=>$sourcetext,
1825                                          'format'=>FORMAT_MOODLE);
1827         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
1828         $plugin->save($submission, $data);
1830         $this->setUser($this->editingteachers[0]);
1832         $data = new stdClass();
1833         require_once($CFG->dirroot . '/mod/assign/gradeform.php');
1834         $pagination = array('userid'=>$this->students[0]->id,
1835                             'rownum'=>0,
1836                             'last'=>true,
1837                             'useridlistid'=>time(),
1838                             'attemptnumber'=>0);
1839         $formparams = array($assign, $data, $pagination);
1840         $mform = new mod_assign_grade_form(null, $formparams);
1842         $this->assertEquals($filteredtext, $data->assignfeedbackcomments_editor['text']);
1843     }
1845     /**
1846      * Testing for feedback comment plugin settings
1847      */
1848     public function test_feedback_plugin_settings() {
1850         $assign = $this->create_instance();
1851         $plugin = $assign->get_feedback_plugin_by_type('comments');
1852         $this->assertEquals(0, $plugin->is_enabled('enabled'));
1854         $assign = $this->create_instance(array('assignfeedback_comments_enabled' => 0));
1855         $plugin = $assign->get_feedback_plugin_by_type('comments');
1856         $this->assertEquals(0, $plugin->is_enabled('enabled'));
1858         $assign = $this->create_instance(array('assignfeedback_comments_enabled' => 1));
1859         $plugin = $assign->get_feedback_plugin_by_type('comments');
1860         $this->assertEquals(1, $plugin->is_enabled('enabled'));
1861     }
1863     /**
1864      * Testing if gradebook feedback plugin is enabled.
1865      */
1866     public function test_is_gradebook_feedback_enabled() {
1867         $adminconfig = get_config('assign');
1868         $gradebookplugin = $adminconfig->feedback_plugin_for_gradebook;
1870         // Create assignment with gradebook feedback enabled and grade = 0.
1871         $assign = $this->create_instance(array($gradebookplugin . '_enabled' => 1, 'grades' => 0));
1873         // Get gradebook feedback plugin.
1874         $gradebookplugintype = str_replace('assignfeedback_', '', $gradebookplugin);
1875         $plugin = $assign->get_feedback_plugin_by_type($gradebookplugintype);
1876         $this->assertEquals(1, $plugin->is_enabled('enabled'));
1877         $this->assertEquals(1, $assign->is_gradebook_feedback_enabled());
1879         // Create assignment with gradebook feedback disabled and grade = 0.
1880         $assign = $this->create_instance(array($gradebookplugin . '_enabled' => 0, 'grades' => 0));
1881         $plugin = $assign->get_feedback_plugin_by_type($gradebookplugintype);
1882         $this->assertEquals(0, $plugin->is_enabled('enabled'));
1883     }
1885     /**
1886      * Testing can_edit_submission
1887      */
1888     public function test_can_edit_submission() {
1889         global $PAGE, $DB;
1890         $this->create_extra_users();
1892         $this->setAdminUser();
1893         // Create assignment (onlinetext).
1894         $assign = $this->create_instance(array('assignsubmission_onlinetext_enabled'=>1, 'submissiondrafts'=>1));
1895         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
1897         // Check student can edit their own submission.
1898         $this->assertTrue($assign->can_edit_submission($this->students[0]->id, $this->students[0]->id));
1899         // Check student cannot edit others submission.
1900         $this->assertFalse($assign->can_edit_submission($this->students[0]->id, $this->students[1]->id));
1902         // Check teacher cannot (by default) edit a students submission.
1903         $this->assertFalse($assign->can_edit_submission($this->students[0]->id, $this->teachers[0]->id));
1905         // Add the required capability to edit a student submission.
1906         $roleid = create_role('Dummy role', 'dummyrole', 'dummy role description');
1907         assign_capability('mod/assign:editothersubmission', CAP_ALLOW, $roleid, $assign->get_context()->id);
1908         role_assign($roleid, $this->teachers[0]->id, $assign->get_context()->id);
1909         accesslib_clear_all_caches_for_unit_testing();
1910         // Retest - should now have access.
1911         $this->assertTrue($assign->can_edit_submission($this->students[0]->id, $this->teachers[0]->id));
1913         // Force create an assignment with SEPARATEGROUPS.
1914         $data = new stdClass();
1915         $data->courseid = $this->course->id;
1916         $data->name = 'Grouping';
1917         $groupingid = groups_create_grouping($data);
1918         groups_assign_grouping($groupingid, $this->groups[0]->id);
1919         groups_assign_grouping($groupingid, $this->groups[1]->id);
1920         $assign = $this->create_instance(array('groupingid' => $groupingid, 'groupmode' => SEPARATEGROUPS));
1922         // Add the capability to the new assignment for extra students 0 and 1.
1923         assign_capability('mod/assign:editothersubmission', CAP_ALLOW, $roleid, $assign->get_context()->id);
1924         role_assign($roleid, $this->extrastudents[0]->id, $assign->get_context()->id);
1925         role_assign($roleid, $this->extrastudents[1]->id, $assign->get_context()->id);
1926         accesslib_clear_all_caches_for_unit_testing();
1928         // Verify the extra student does not have the capability to edit a submission not in their group.
1929         $this->assertFalse($assign->can_edit_submission($this->students[0]->id, $this->extrastudents[1]->id));
1930         // Verify the extra student does have the capability to edit a submission in their group.
1931         $this->assertTrue($assign->can_edit_submission($this->students[0]->id, $this->extrastudents[0]->id));
1933     }
1935     /**
1936      * Test if the view blind details capability works
1937      */
1938     public function test_can_view_blind_details() {
1939         global $PAGE, $DB;
1940         $teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
1941         $managerrole = $DB->get_record('role', array('shortname' => 'manager'));
1943         $student = $this->students[0];// Get a student user.
1944         // Create a teacher. Shouldn't be able to view blind marking ID.
1945         $teacher = $this->getDataGenerator()->create_user();
1947         $this->getDataGenerator()->enrol_user($teacher->id,
1948                                               $this->course->id,
1949                                               $teacherrole->id);
1951         // Create a manager.. Should be able to view blind marking ID.
1952         $manager = $this->getDataGenerator()->create_user();
1953         $this->getDataGenerator()->enrol_user($manager->id,
1954                 $this->course->id,
1955                 $managerrole->id);
1957         // Generate blind marking assignment.
1958         $assign = $this->create_instance(array('blindmarking' => 1));
1959         $this->assertEquals(true, $assign->is_blind_marking());
1961         // Test student names are hidden to teacher.
1962         $this->setUser($teacher);
1963         $gradingtable = new assign_grading_table($assign, 1, '', 0, true);
1964         $output = $assign->get_renderer()->render($gradingtable);
1965         $this->assertEquals(true, strpos($output, get_string('hiddenuser', 'assign')));    // "Participant" is somewhere on the page.
1966         $this->assertEquals(false, strpos($output, fullname($student)));    // Students full name doesn't appear.
1968         // Test student names are visible to manager.
1969         $this->setUser($manager);
1970         $gradingtable = new assign_grading_table($assign, 1, '', 0, true);
1971         $output = $assign->get_renderer()->render($gradingtable);
1972         $this->assertEquals(false, strpos($output, get_string('hiddenuser', 'assign')));
1973         $this->assertEquals(true, strpos($output, fullname($student)));    //students full name doesn't appear.
1974     }