MDL-48778 assign: Use proper latest attempt for quickgrading prechecks
[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         $assign1 = $this->create_instance(array('teamsubmission' => 1));
637         $this->assertEquals(self::GROUP_COUNT + 1, $assign1->count_teams());
639         $grouping = $this->getDataGenerator()->create_grouping(array('courseid' => $this->course->id));
640         $this->getDataGenerator()->create_grouping_group(array('groupid' => $this->groups[0]->id, 'groupingid' => $grouping->id));
641         $this->getDataGenerator()->create_grouping_group(array('groupid' => $this->groups[1]->id, 'groupingid' => $grouping->id));
643         // No active group and non group submissions allowed => 2 groups + the default one.
644         $params = array(
645             'teamsubmission' => 1,
646             'teamsubmissiongroupingid' => $grouping->id,
647             'preventsubmissionnotingroup' => false
648         );
649         $assign2 = $this->create_instance($params);
650         $this->assertEquals(3, $assign2->count_teams());
652         // An active group => Just the selected one.
653         $this->assertEquals(1, $assign2->count_teams($this->groups[0]->id));
655         // No active group and non group submissions allowed => 2 groups + no default one.
656         $params = array('teamsubmission' => 1, 'teamsubmissiongroupingid' => $grouping->id, 'preventsubmissionnotingroup' => true);
657         $assign3 = $this->create_instance($params);
658         $this->assertEquals(2, $assign3->count_teams());
660         $assign4 = $this->create_instance(array('teamsubmission' => 1, 'preventsubmissionnotingroup' => true));
661         $this->assertEquals(self::GROUP_COUNT, $assign4->count_teams());
662     }
664     public function test_submit_to_default_group() {
665         global $DB, $SESSION;
667         $this->preventResetByRollback();
668         $sink = $this->redirectMessages();
670         $this->setUser($this->editingteachers[0]);
671         $params = array('teamsubmission' => 1,
672                         'assignsubmission_onlinetext_enabled' => 1,
673                         'submissiondrafts' => 0,
674                         'groupmode' => VISIBLEGROUPS);
675         $assign = $this->create_instance($params);
677         $newstudent = $this->getDataGenerator()->create_user();
678         $studentrole = $DB->get_record('role', array('shortname'=>'student'));
679         $this->getDataGenerator()->enrol_user($newstudent->id,
680                                               $this->course->id,
681                                               $studentrole->id);
682         $this->setUser($newstudent);
683         $data = new stdClass();
684         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
685                                          'text'=>'Submission text',
686                                          'format'=>FORMAT_MOODLE);
687         $notices = array();
689         $group = $assign->get_submission_group($newstudent->id);
690         $this->assertFalse($group, 'New student is in default group');
691         $assign->save_submission($data, $notices);
692         $this->assertEmpty($notices, 'No errors on save submission');
694         // Set active groups to all groups.
695         $this->setUser($this->teachers[0]);
696         $SESSION->activegroup[$this->course->id]['aag'][0] = 0;
697         $this->assertEquals(1, $assign->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_SUBMITTED));
699         // Set an active group.
700         $anothergroup = $this->groups[0];
701         $SESSION->activegroup[$this->course->id]['aag'][0] = (int)$anothergroup->id;
702         $this->assertEquals(0, $assign->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_SUBMITTED));
704         $sink->close();
705     }
707     public function test_count_submissions() {
708         global $SESSION;
710         $this->create_extra_users();
711         $this->setUser($this->editingteachers[0]);
712         $assign1 = $this->create_instance(array('assignsubmission_onlinetext_enabled' => 1));
714         // Simulate a submission.
715         $this->setUser($this->extrastudents[0]);
716         $submission = $assign1->get_user_submission($this->extrastudents[0]->id, true);
717         $submission->status = ASSIGN_SUBMISSION_STATUS_DRAFT;
718         $assign1->testable_update_submission($submission, $this->extrastudents[0]->id, true, false);
719         // Leave this one as DRAFT.
720         $data = new stdClass();
721         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
722                                          'text'=>'Submission text',
723                                          'format'=>FORMAT_MOODLE);
724         $plugin = $assign1->get_submission_plugin_by_type('onlinetext');
725         $plugin->save($submission, $data);
727         // Simulate adding a grade.
728         $this->setUser($this->teachers[0]);
729         $data = new stdClass();
730         $data->grade = '50.0';
731         $assign1->testable_apply_grade_to_user($data, $this->extrastudents[0]->id, 0);
733         // Simulate a submission.
734         $this->setUser($this->extrastudents[1]);
735         $submission = $assign1->get_user_submission($this->extrastudents[1]->id, true);
736         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
737         $assign1->testable_update_submission($submission, $this->extrastudents[1]->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 = $assign1->get_submission_plugin_by_type('onlinetext');
743         $plugin->save($submission, $data);
745         // Simulate a submission.
746         $this->setUser($this->extrastudents[2]);
747         $submission = $assign1->get_user_submission($this->extrastudents[2]->id, true);
748         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
749         $assign1->testable_update_submission($submission, $this->extrastudents[2]->id, true, false);
750         $data = new stdClass();
751         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
752                                          'text'=>'Submission text',
753                                          'format'=>FORMAT_MOODLE);
754         $plugin = $assign1->get_submission_plugin_by_type('onlinetext');
755         $plugin->save($submission, $data);
757         // Simulate a submission.
758         $this->setUser($this->extrastudents[3]);
759         $submission = $assign1->get_user_submission($this->extrastudents[3]->id, true);
760         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
761         $assign1->testable_update_submission($submission, $this->extrastudents[3]->id, true, false);
762         $data = new stdClass();
763         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
764                                          'text'=>'Submission text',
765                                          'format'=>FORMAT_MOODLE);
766         $plugin = $assign1->get_submission_plugin_by_type('onlinetext');
767         $plugin->save($submission, $data);
769         // Simulate a submission for suspended user, this will never be counted.
770         $this->setUser($this->extrastudents[3]);
771         $submission = $assign1->get_user_submission($this->extrasuspendedstudents[0]->id, true);
772         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
773         $assign1->testable_update_submission($submission, $this->extrasuspendedstudents[0]->id, true, false);
774         $data = new stdClass();
775         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
776                                          'text'=>'Submission text',
777                                          'format'=>FORMAT_MOODLE);
778         $plugin = $assign1->get_submission_plugin_by_type('onlinetext');
779         $plugin->save($submission, $data);
781         // Wait 1 second so the submission and grade do not have the same timemodified.
782         sleep(1);
783         // Simulate adding a grade.
784         $this->setUser($this->teachers[0]);
785         $data = new stdClass();
786         $data->grade = '50.0';
787         $assign1->testable_apply_grade_to_user($data, $this->extrastudents[3]->id, 0);
788         $assign1->testable_apply_grade_to_user($data, $this->extrasuspendedstudents[0]->id, 0);
790         // Create a new submission with status NEW.
791         $this->setUser($this->extrastudents[4]);
792         $submission = $assign1->get_user_submission($this->extrastudents[4]->id, true);
794         $this->assertEquals(2, $assign1->count_grades());
795         $this->assertEquals(4, $assign1->count_submissions());
796         $this->assertEquals(5, $assign1->count_submissions(true));
797         $this->assertEquals(2, $assign1->count_submissions_need_grading());
798         $this->assertEquals(3, $assign1->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_SUBMITTED));
799         $this->assertEquals(1, $assign1->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_DRAFT));
801         // Groups.
802         $assign2 = $this->create_instance(array(
803             'assignsubmission_onlinetext_enabled' => 1,
804             'groupmode' => VISIBLEGROUPS
805         ));
807         $this->setUser($this->extrastudents[1]);
808         $submission = $assign2->get_user_submission($this->extrastudents[1]->id, true);
809         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
810         $assign2->testable_update_submission($submission, $this->extrastudents[1]->id, true, false);
811         $data = new stdClass();
812         $data->onlinetext_editor = array('itemid' => file_get_unused_draft_itemid(),
813                                          'text' => 'Submission text',
814                                          'format' => FORMAT_MOODLE);
815         $plugin = $assign2->get_submission_plugin_by_type('onlinetext');
816         $plugin->save($submission, $data);
818         $this->assertEquals(1, $assign2->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_SUBMITTED));
820         // Set active groups to all groups.
821         $this->setUser($this->teachers[0]);
822         $SESSION->activegroup[$this->course->id]['aag'][0] = 0;
823         $this->assertEquals(1, $assign2->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_SUBMITTED));
825         // Set the user group.
826         $studentgroups = groups_get_user_groups($this->course->id, $this->extrastudents[1]->id);
827         $this->assertEquals(1, count($studentgroups));
828         $studentgroup = array_pop($studentgroups);
829         $SESSION->activegroup[$this->course->id]['aag'][0] = $studentgroup[0];
830         $this->assertEquals(1, $assign2->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_SUBMITTED));
832         // Set another group.
833         $anothergroup = $this->groups[0];
834         $this->assertNotEquals($anothergroup->id, $studentgroup[0]);
835         $SESSION->activegroup[$this->course->id]['aag'][0] = (int)$anothergroup->id;
836         $this->assertEquals(0, $assign2->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_SUBMITTED));
837     }
839     public function test_count_submissions_for_groups() {
840         $this->create_extra_users();
841         $groupid = null;
842         $this->setUser($this->editingteachers[0]);
843         $assign = $this->create_instance(array('assignsubmission_onlinetext_enabled' => 1, 'teamsubmission' => 1));
845         // Simulate a submission.
846         $this->setUser($this->extrastudents[0]);
847         $submission = $assign->get_group_submission($this->extrastudents[0]->id, $groupid, true);
848         $submission->status = ASSIGN_SUBMISSION_STATUS_DRAFT;
849         $assign->testable_update_submission($submission, $this->extrastudents[0]->id, true, false);
850         // Leave this one as DRAFT.
851         $data = new stdClass();
852         $data->onlinetext_editor = array('itemid' => file_get_unused_draft_itemid(),
853                                          'text' => 'Submission text',
854                                          'format' => FORMAT_MOODLE);
855         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
856         $plugin->save($submission, $data);
858         // Simulate adding a grade.
859         $this->setUser($this->teachers[0]);
860         $data = new stdClass();
861         $data->grade = '50.0';
862         $assign->testable_apply_grade_to_user($data, $this->extrastudents[0]->id, 0);
864         // Simulate a submission.
865         $this->setUser($this->extrastudents[1]);
866         $submission = $assign->get_group_submission($this->extrastudents[1]->id, $groupid, true);
867         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
868         $assign->testable_update_submission($submission, $this->extrastudents[1]->id, true, false);
869         $data = new stdClass();
870         $data->onlinetext_editor = array('itemid' => file_get_unused_draft_itemid(),
871                                          'text' => 'Submission text',
872                                          'format' => FORMAT_MOODLE);
873         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
874         $plugin->save($submission, $data);
876         // Simulate a submission.
877         $this->setUser($this->extrastudents[2]);
878         $submission = $assign->get_group_submission($this->extrastudents[2]->id, $groupid, true);
879         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
880         $assign->testable_update_submission($submission, $this->extrastudents[2]->id, true, false);
881         $data = new stdClass();
882         $data->onlinetext_editor = array('itemid' => file_get_unused_draft_itemid(),
883                                          'text' => 'Submission text',
884                                          'format' => FORMAT_MOODLE);
885         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
886         $plugin->save($submission, $data);
888         // Simulate a submission.
889         $this->setUser($this->extrastudents[3]);
890         $submission = $assign->get_group_submission($this->extrastudents[3]->id, $groupid, true);
891         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
892         $assign->testable_update_submission($submission, $this->extrastudents[3]->id, true, false);
893         $data = new stdClass();
894         $data->onlinetext_editor = array('itemid' => file_get_unused_draft_itemid(),
895                                          'text' => 'Submission text',
896                                          'format' => FORMAT_MOODLE);
897         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
898         $plugin->save($submission, $data);
900         // Simulate adding a grade.
901         $this->setUser($this->teachers[0]);
902         $data = new stdClass();
903         $data->grade = '50.0';
904         $assign->testable_apply_grade_to_user($data, $this->extrastudents[3]->id, 0);
905         $assign->testable_apply_grade_to_user($data, $this->extrasuspendedstudents[0]->id, 0);
907         // Create a new submission with status NEW.
908         $this->setUser($this->extrastudents[4]);
909         $submission = $assign->get_group_submission($this->extrastudents[4]->id, $groupid, true);
911         $this->assertEquals(2, $assign->count_grades());
912         $this->assertEquals(4, $assign->count_submissions());
913         $this->assertEquals(5, $assign->count_submissions(true));
914         $this->assertEquals(3, $assign->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_SUBMITTED));
915         $this->assertEquals(1, $assign->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_DRAFT));
916     }
918     public function test_get_grading_userid_list() {
919         $this->create_extra_users();
920         $this->setUser($this->editingteachers[0]);
921         $assign = $this->create_instance();
923         $users = $assign->testable_get_grading_userid_list();
924         $this->assertEquals(self::DEFAULT_STUDENT_COUNT + self::EXTRA_STUDENT_COUNT, count($users));
926         $this->setUser($this->editingteachers[0]);
927         set_user_preference('grade_report_showonlyactiveenrol', false);
928         $assign = $this->create_instance();
930         $users = $assign->testable_get_grading_userid_list();
931         $this->assertEquals(self::DEFAULT_STUDENT_COUNT + self::EXTRA_STUDENT_COUNT + self::EXTRA_SUSPENDED_COUNT, count($users));
932     }
934     public function test_cron() {
935         // First run cron so there are no messages waiting to be sent (from other tests).
936         cron_setup_user();
937         assign::cron();
939         // Now create an assignment and add some feedback.
940         $this->setUser($this->editingteachers[0]);
941         $assign = $this->create_instance(array('sendstudentnotifications'=>1));
943         // Simulate adding a grade.
944         $this->setUser($this->teachers[0]);
945         $data = new stdClass();
946         $data->grade = '50.0';
947         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
948         $assign->testable_apply_grade_to_user($data, $this->students[1]->id, 0);
950         $data->sendstudentnotifications = false;
951         $assign->testable_apply_grade_to_user($data, $this->students[2]->id, 0);
953         // Now run cron and see that one message was sent.
954         $this->preventResetByRollback();
955         $sink = $this->redirectMessages();
956         cron_setup_user();
957         $this->expectOutputRegex('/Done processing 2 assignment submissions/');
958         assign::cron();
960         $messages = $sink->get_messages();
961         // The sent count should be 2, because the 3rd one was marked as do not send notifications.
962         $this->assertEquals(2, count($messages));
963         $this->assertEquals(1, $messages[0]->notification);
964         $this->assertEquals($assign->get_instance()->name, $messages[0]->contexturlname);
966         // Regrading a grade causes a notification to the user.
967         $data->sendstudentnotifications = true;
968         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
969         assign::cron();
970         $messages = $sink->get_messages();
971         $this->assertEquals(3, count($messages));
972     }
974     /**
975      * Test delivery of grade notifications as controlled by marking workflow.
976      */
977     public function test_markingworkflow_cron() {
978         // First run cron so there are no messages waiting to be sent (from other tests).
979         cron_setup_user();
980         assign::cron();
982         // Now create an assignment with marking workflow enabled.
983         $this->setUser($this->editingteachers[0]);
984         $assign = $this->create_instance(array('sendstudentnotifications' => 1, 'markingworkflow' => 1));
986         // Simulate adding a grade.
987         $this->setUser($this->teachers[0]);
988         $data = new stdClass();
989         $data->grade = '50.0';
991         // This student will not receive notification.
992         $data->workflowstate = ASSIGN_MARKING_WORKFLOW_STATE_READYFORRELEASE;
993         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
995         // This student will receive notification.
996         $data->workflowstate = ASSIGN_MARKING_WORKFLOW_STATE_RELEASED;
997         $assign->testable_apply_grade_to_user($data, $this->students[1]->id, 0);
999         // Now run cron and see that one message was sent.
1000         $this->preventResetByRollback();
1001         $sink = $this->redirectMessages();
1002         cron_setup_user();
1003         $this->expectOutputRegex('/Done processing 1 assignment submissions/');
1004         assign::cron();
1006         $messages = $sink->get_messages();
1007         $this->assertEquals(1, count($messages));
1008         $this->assertEquals($messages[0]->useridto, $this->students[1]->id);
1009         $this->assertEquals($assign->get_instance()->name, $messages[0]->contexturlname);
1010     }
1012     public function test_is_graded() {
1013         $this->setUser($this->editingteachers[0]);
1014         $assign = $this->create_instance();
1016         // Simulate adding a grade.
1017         $this->setUser($this->teachers[0]);
1018         $data = new stdClass();
1019         $data->grade = '50.0';
1020         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
1022         $this->assertEquals(true, $assign->testable_is_graded($this->students[0]->id));
1023         $this->assertEquals(false, $assign->testable_is_graded($this->students[1]->id));
1024     }
1026     public function test_can_grade() {
1027         global $DB;
1029         $this->setUser($this->editingteachers[0]);
1030         $assign = $this->create_instance();
1032         $this->setUser($this->students[0]);
1033         $this->assertEquals(false, $assign->can_grade());
1034         $this->setUser($this->editingteachers[0]);
1035         $this->assertEquals(true, $assign->can_grade());
1036         $this->setUser($this->teachers[0]);
1037         $this->assertEquals(true, $assign->can_grade());
1039         // Test the viewgrades capability - without mod/assign:grade.
1040         $this->setUser($this->students[0]);
1041         $studentrole = $DB->get_record('role', array('shortname' => 'student'));
1042         assign_capability('mod/assign:viewgrades', CAP_ALLOW, $studentrole->id, $assign->get_context()->id);
1043         $this->assertEquals(false, $assign->can_grade());
1044     }
1046     public function test_can_view_submission() {
1047         global $DB;
1049         $this->create_extra_users();
1050         $this->setUser($this->editingteachers[0]);
1051         $assign = $this->create_instance();
1053         $this->setUser($this->students[0]);
1054         $this->assertEquals(true, $assign->can_view_submission($this->students[0]->id));
1055         $this->assertEquals(false, $assign->can_view_submission($this->students[1]->id));
1056         $this->assertEquals(false, $assign->can_view_submission($this->teachers[0]->id));
1057         $this->setUser($this->teachers[0]);
1058         $this->assertEquals(true, $assign->can_view_submission($this->students[0]->id));
1059         $this->assertEquals(true, $assign->can_view_submission($this->students[1]->id));
1060         $this->assertEquals(true, $assign->can_view_submission($this->teachers[0]->id));
1061         $this->assertEquals(false, $assign->can_view_submission($this->extrasuspendedstudents[0]->id));
1062         $this->setUser($this->editingteachers[0]);
1063         $this->assertEquals(true, $assign->can_view_submission($this->students[0]->id));
1064         $this->assertEquals(true, $assign->can_view_submission($this->students[1]->id));
1065         $this->assertEquals(true, $assign->can_view_submission($this->teachers[0]->id));
1066         $this->assertEquals(true, $assign->can_view_submission($this->extrasuspendedstudents[0]->id));
1068         // Test the viewgrades capability - without mod/assign:grade.
1069         $this->setUser($this->students[0]);
1070         $studentrole = $DB->get_record('role', array('shortname' => 'student'));
1071         assign_capability('mod/assign:viewgrades', CAP_ALLOW, $studentrole->id, $assign->get_context()->id);
1072         $this->assertEquals(true, $assign->can_view_submission($this->students[0]->id));
1073         $this->assertEquals(true, $assign->can_view_submission($this->students[1]->id));
1074         $this->assertEquals(true, $assign->can_view_submission($this->teachers[0]->id));
1075         $this->assertEquals(false, $assign->can_view_submission($this->extrasuspendedstudents[0]->id));
1076     }
1079     public function test_update_submission() {
1080         $this->create_extra_users();
1081         $this->setUser($this->editingteachers[0]);
1082         $assign = $this->create_instance();
1084         $this->setUser($this->extrastudents[0]);
1085         $now = time();
1086         $submission = $assign->get_user_submission($this->extrastudents[0]->id, true);
1087         $assign->testable_update_submission($submission, $this->extrastudents[0]->id, true, false);
1089         $this->setUser($this->teachers[0]);
1090         // Verify the gradebook update.
1091         $gradinginfo = grade_get_grades($this->course->id,
1092                                         'mod',
1093                                         'assign',
1094                                         $assign->get_instance()->id,
1095                                         $this->extrastudents[0]->id);
1097         $this->assertEquals($this->extrastudents[0]->id,
1098                             $gradinginfo->items[0]->grades[$this->extrastudents[0]->id]->usermodified);
1100         // Now verify group assignments.
1101         $this->setUser($this->editingteachers[0]);
1102         $assign = $this->create_instance(array('teamsubmission'=>1));
1104         $this->setUser($this->extrastudents[0]);
1105         $now = time();
1106         $submission = $assign->get_group_submission($this->extrastudents[0]->id, 0, true);
1107         $assign->testable_update_submission($submission, $this->extrastudents[0]->id, true, true);
1109         // Check that at least 2 active members and 1 suspended member of the submission group had their submission updated.
1111         $this->setUser($this->editingteachers[0]);
1112         $gradinginfo = grade_get_grades($this->course->id,
1113                                         'mod',
1114                                         'assign',
1115                                         $assign->get_instance()->id,
1116                                         $this->extrastudents[0]->id);
1118         $this->assertEquals($this->extrastudents[0]->id,
1119                             $gradinginfo->items[0]->grades[$this->extrastudents[0]->id]->usermodified);
1121         $gradinginfo = grade_get_grades($this->course->id,
1122                                         'mod',
1123                                         'assign',
1124                                         $assign->get_instance()->id,
1125                                         $this->extrastudents[self::GROUP_COUNT]->id);
1127         $this->assertEquals($this->extrastudents[self::GROUP_COUNT]->id,
1128                             $gradinginfo->items[0]->grades[$this->extrastudents[self::GROUP_COUNT]->id]->usermodified);
1130         $gradinginfo = grade_get_grades($this->course->id,
1131                                         'mod',
1132                                         'assign',
1133                                         $assign->get_instance()->id,
1134                                         $this->extrasuspendedstudents[0]->id);
1135         $this->assertEquals($this->extrasuspendedstudents[0]->id,
1136                             $gradinginfo->items[0]->grades[$this->extrasuspendedstudents[0]->id]->usermodified);
1138         // Check the same with non-editing teacher and make sure submission is not updated for suspended user.
1139         $this->setUser($this->editingteachers[0]);
1140         $assign = $this->create_instance(array('teamsubmission'=>1));
1142         $this->setUser($this->extrastudents[1]);
1143         $now = time();
1144         $submission = $assign->get_group_submission($this->extrastudents[1]->id, 0, true);
1145         $assign->testable_update_submission($submission, $this->extrastudents[1]->id, true, true);
1147         $this->setUser($this->teachers[0]);
1148         $gradinginfo = grade_get_grades($this->course->id,
1149                                         'mod',
1150                                         'assign',
1151                                         $assign->get_instance()->id,
1152                                         $this->extrastudents[1]->id);
1154         $this->assertEquals($this->extrastudents[1]->id,
1155                             $gradinginfo->items[0]->grades[$this->extrastudents[1]->id]->usermodified);
1157         $gradinginfo = grade_get_grades($this->course->id,
1158                                         'mod',
1159                                         'assign',
1160                                         $assign->get_instance()->id,
1161                                         $this->extrastudents[self::GROUP_COUNT+1]->id);
1163         $this->assertEquals($this->extrastudents[self::GROUP_COUNT+1]->id,
1164                             $gradinginfo->items[0]->grades[$this->extrastudents[self::GROUP_COUNT+1]->id]->usermodified);
1166         $gradinginfo = grade_get_grades($this->course->id,
1167                                         'mod',
1168                                         'assign',
1169                                         $assign->get_instance()->id,
1170                                         $this->extrasuspendedstudents[1]->id);
1171         $this->assertEquals($this->extrasuspendedstudents[1]->id,
1172                             $gradinginfo->items[0]->grades[$this->extrasuspendedstudents[1]->id]->usermodified);
1174         // Now verify blind marking.
1175         $this->setUser($this->editingteachers[0]);
1176         $assign = $this->create_instance(array('blindmarking'=>1));
1178         $this->setUser($this->extrastudents[0]);
1179         $now = time();
1180         $submission = $assign->get_user_submission($this->extrastudents[0]->id, true);
1181         $assign->testable_update_submission($submission, $this->extrastudents[0]->id, true, false);
1183         $this->setUser($this->editingteachers[0]);
1184         $gradinginfo = grade_get_grades($this->course->id,
1185                                         'mod',
1186                                         'assign',
1187                                         $assign->get_instance()->id,
1188                                         $this->extrastudents[0]->id);
1190         $this->assertEquals(null, $gradinginfo->items[0]->grades[$this->extrastudents[0]->id]->datesubmitted);
1191     }
1193     public function test_group_submissions_submit_for_marking_requireallteammemberssubmit() {
1194         global $PAGE;
1196         $this->create_extra_users();
1197         // Now verify group assignments.
1198         $this->setUser($this->editingteachers[0]);
1199         $assign = $this->create_instance(array('teamsubmission'=>1,
1200                                                'assignsubmission_onlinetext_enabled'=>1,
1201                                                'submissiondrafts'=>1,
1202                                                'requireallteammemberssubmit'=>1));
1203         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
1205         // Add a submission.
1206         $this->setUser($this->extrastudents[0]);
1207         $data = new stdClass();
1208         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
1209                                          'text'=>'Submission text',
1210                                          'format'=>FORMAT_MOODLE);
1212         $notices = array();
1213         $assign->save_submission($data, $notices);
1215         // Check we can see the submit button.
1216         $output = $assign->view_student_summary($this->extrastudents[0], true);
1217         $this->assertContains(get_string('submitassignment', 'assign'), $output);
1219         $submission = $assign->get_group_submission($this->extrastudents[0]->id, 0, true);
1220         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
1221         $assign->testable_update_submission($submission, $this->extrastudents[0]->id, true, true);
1223         // Check that the student does not see "Submit" button.
1224         $output = $assign->view_student_summary($this->extrastudents[0], true);
1225         $this->assertNotContains(get_string('submitassignment', 'assign'), $output);
1227         // Change to another user in the same group.
1228         $this->setUser($this->extrastudents[self::GROUP_COUNT]);
1229         $output = $assign->view_student_summary($this->extrastudents[self::GROUP_COUNT], true);
1230         $this->assertContains(get_string('submitassignment', 'assign'), $output);
1232         $submission = $assign->get_group_submission($this->extrastudents[self::GROUP_COUNT]->id, 0, true);
1233         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
1234         $assign->testable_update_submission($submission, $this->extrastudents[self::GROUP_COUNT]->id, true, true);
1235         $output = $assign->view_student_summary($this->extrastudents[self::GROUP_COUNT], true);
1236         $this->assertNotContains(get_string('submitassignment', 'assign'), $output);
1237     }
1239     public function test_group_submissions_submit_for_marking() {
1240         global $PAGE;
1242         $this->create_extra_users();
1243         // Now verify group assignments.
1244         $this->setUser($this->editingteachers[0]);
1245         $time = time();
1246         $assign = $this->create_instance(array('teamsubmission'=>1,
1247                                                'assignsubmission_onlinetext_enabled'=>1,
1248                                                'submissiondrafts'=>1,
1249                                                'requireallteammemberssubmit'=>0,
1250                                                'duedate' => $time - 2*24*60*60));
1251         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
1253         $this->setUser($this->extrastudents[0]);
1254         // Add a submission.
1255         $data = new stdClass();
1256         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
1257                                          'text'=>'Submission text',
1258                                          'format'=>FORMAT_MOODLE);
1260         $notices = array();
1261         $assign->save_submission($data, $notices);
1263         // Check we can see the submit button.
1264         $output = $assign->view_student_summary($this->extrastudents[0], true);
1265         $this->assertContains(get_string('submitassignment', 'assign'), $output);
1266         $this->assertContains(get_string('timeremaining', 'assign'), $output);
1267         $difftime = time() - $time;
1268         $this->assertContains(get_string('overdue', 'assign', format_time(2*24*60*60 + $difftime)), $output);
1270         $submission = $assign->get_group_submission($this->extrastudents[0]->id, 0, true);
1271         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
1272         $assign->testable_update_submission($submission, $this->extrastudents[0]->id, true, true);
1274         // Check that the student does not see "Submit" button.
1275         $output = $assign->view_student_summary($this->extrastudents[0], true);
1276         $this->assertNotContains(get_string('submitassignment', 'assign'), $output);
1278         // Change to another user in the same group.
1279         $this->setUser($this->extrastudents[self::GROUP_COUNT]);
1280         $output = $assign->view_student_summary($this->extrastudents[self::GROUP_COUNT], true);
1281         $this->assertNotContains(get_string('submitassignment', 'assign'), $output);
1283         // Check that time remaining is not overdue.
1284         $this->assertContains(get_string('timeremaining', 'assign'), $output);
1285         $difftime = time() - $time;
1286         $this->assertContains(get_string('submittedlate', 'assign', format_time(2*24*60*60 + $difftime)), $output);
1288         $submission = $assign->get_group_submission($this->extrastudents[self::GROUP_COUNT]->id, 0, true);
1289         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
1290         $assign->testable_update_submission($submission, $this->extrastudents[self::GROUP_COUNT]->id, true, true);
1291         $output = $assign->view_student_summary($this->extrastudents[self::GROUP_COUNT], true);
1292         $this->assertNotContains(get_string('submitassignment', 'assign'), $output);
1293     }
1295     public function test_submissions_open() {
1296         $this->setUser($this->editingteachers[0]);
1298         $now = time();
1299         $tomorrow = $now + 24*60*60;
1300         $oneweek = $now + 7*24*60*60;
1301         $yesterday = $now - 24*60*60;
1303         $assign = $this->create_instance();
1304         $this->assertEquals(true, $assign->testable_submissions_open($this->students[0]->id));
1306         $assign = $this->create_instance(array('duedate'=>$tomorrow));
1307         $this->assertEquals(true, $assign->testable_submissions_open($this->students[0]->id));
1309         $assign = $this->create_instance(array('duedate'=>$yesterday));
1310         $this->assertEquals(true, $assign->testable_submissions_open($this->students[0]->id));
1312         $assign = $this->create_instance(array('duedate'=>$yesterday, 'cutoffdate'=>$tomorrow));
1313         $this->assertEquals(true, $assign->testable_submissions_open($this->students[0]->id));
1315         $assign = $this->create_instance(array('duedate'=>$yesterday, 'cutoffdate'=>$yesterday));
1316         $this->assertEquals(false, $assign->testable_submissions_open($this->students[0]->id));
1318         $assign->testable_save_user_extension($this->students[0]->id, $tomorrow);
1319         $this->assertEquals(true, $assign->testable_submissions_open($this->students[0]->id));
1321         $assign = $this->create_instance(array('submissiondrafts'=>1));
1322         $this->assertEquals(true, $assign->testable_submissions_open($this->students[0]->id));
1324         $this->setUser($this->students[0]);
1325         $now = time();
1326         $submission = $assign->get_user_submission($this->students[0]->id, true);
1327         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
1328         $assign->testable_update_submission($submission, $this->students[0]->id, true, false);
1329         $this->setUser($this->editingteachers[0]);
1330         $this->assertEquals(false, $assign->testable_submissions_open($this->students[0]->id));
1331     }
1333     public function test_get_graders() {
1334         $this->create_extra_users();
1335         $this->setUser($this->editingteachers[0]);
1337         // Create an assignment with no groups.
1338         $assign = $this->create_instance();
1339         $this->assertCount(self::DEFAULT_TEACHER_COUNT +
1340                            self::DEFAULT_EDITING_TEACHER_COUNT +
1341                            self::EXTRA_TEACHER_COUNT +
1342                            self::EXTRA_EDITING_TEACHER_COUNT,
1343                            $assign->testable_get_graders($this->students[0]->id));
1345         // Force create an assignment with SEPARATEGROUPS.
1346         $data = new stdClass();
1347         $data->courseid = $this->course->id;
1348         $data->name = 'Grouping';
1349         $groupingid = groups_create_grouping($data);
1350         groups_assign_grouping($groupingid, $this->groups[0]->id);
1351         $assign = $this->create_instance(array('groupingid' => $groupingid, 'groupmode' => SEPARATEGROUPS));
1353         $this->setUser($this->students[1]);
1354         $this->assertCount(4, $assign->testable_get_graders($this->students[0]->id));
1355         // Note the second student is in a group that is not in the grouping.
1356         // This means that we get all graders that are not in a group in the grouping.
1357         $this->assertCount(10, $assign->testable_get_graders($this->students[1]->id));
1358     }
1360     public function test_get_notified_users() {
1361         global $CFG, $DB;
1363         $capability = 'mod/assign:receivegradernotifications';
1364         $coursecontext = context_course::instance($this->course->id);
1365         $role = $DB->get_record('role', array('shortname' => 'teacher'));
1367         $this->create_extra_users();
1368         $this->setUser($this->editingteachers[0]);
1370         // Create an assignment with no groups.
1371         $assign = $this->create_instance();
1373         $this->assertCount(self::DEFAULT_TEACHER_COUNT +
1374                            self::DEFAULT_EDITING_TEACHER_COUNT +
1375                            self::EXTRA_TEACHER_COUNT +
1376                            self::EXTRA_EDITING_TEACHER_COUNT,
1377                            $assign->testable_get_notifiable_users($this->students[0]->id));
1379         // Change nonediting teachers role to not receive grader notifications.
1380         assign_capability($capability, CAP_PROHIBIT, $role->id, $coursecontext);
1382         $this->assertCount(self::DEFAULT_EDITING_TEACHER_COUNT +
1383                            self::EXTRA_EDITING_TEACHER_COUNT,
1384                            $assign->testable_get_notifiable_users($this->students[0]->id));
1386         // Reset nonediting teachers role to default.
1387         unassign_capability($capability, $role->id, $coursecontext);
1389         // Force create an assignment with SEPARATEGROUPS.
1390         $data = new stdClass();
1391         $data->courseid = $this->course->id;
1392         $data->name = 'Grouping';
1393         $groupingid = groups_create_grouping($data);
1394         groups_assign_grouping($groupingid, $this->groups[0]->id);
1395         $assign = $this->create_instance(array('groupingid' => $groupingid, 'groupmode' => SEPARATEGROUPS));
1397         $this->setUser($this->students[1]);
1398         $this->assertCount(4, $assign->testable_get_notifiable_users($this->students[0]->id));
1399         // Note the second student is in a group that is not in the grouping.
1400         // This means that we get all graders that are not in a group in the grouping.
1401         $this->assertCount(10, $assign->testable_get_notifiable_users($this->students[1]->id));
1403         // Change nonediting teachers role to not receive grader notifications.
1404         assign_capability($capability, CAP_PROHIBIT, $role->id, $coursecontext);
1406         $this->assertCount(2, $assign->testable_get_notifiable_users($this->students[0]->id));
1407         // Note the second student is in a group that is not in the grouping.
1408         // This means that we get all graders that are not in a group in the grouping.
1409         $this->assertCount(5, $assign->testable_get_notifiable_users($this->students[1]->id));
1410     }
1412     public function test_group_members_only() {
1413         global $CFG;
1415         $this->setAdminUser();
1416         $this->create_extra_users();
1417         $CFG->enableavailability = true;
1418         $grouping = $this->getDataGenerator()->create_grouping(array('courseid' => $this->course->id));
1419         groups_assign_grouping($grouping->id, $this->groups[0]->id);
1421         // Force create an assignment with SEPARATEGROUPS.
1422         $instance = $this->getDataGenerator()->create_module('assign', array('course'=>$this->course->id),
1423                 array('availability' => json_encode(\core_availability\tree::get_root_json(array(
1424                     \availability_grouping\condition::get_json()))),
1425                 'groupingid' => $grouping->id));
1427         $cm = get_coursemodule_from_instance('assign', $instance->id);
1428         $context = context_module::instance($cm->id);
1429         $assign = new testable_assign($context, $cm, $this->course);
1431         $this->setUser($this->teachers[0]);
1432         get_fast_modinfo($this->course, 0, true);
1433         $this->assertCount(5, $assign->list_participants(0, true));
1435     }
1437     public function test_get_uniqueid_for_user() {
1438         $this->setUser($this->editingteachers[0]);
1439         $assign = $this->create_instance();
1441         foreach ($this->students as $student) {
1442             $uniqueid = $assign->get_uniqueid_for_user($student->id);
1443             $this->assertEquals($student->id, $assign->get_user_id_for_uniqueid($uniqueid));
1444         }
1445     }
1447     public function test_show_student_summary() {
1448         global $CFG, $PAGE;
1450         $this->setUser($this->editingteachers[0]);
1451         $assign = $this->create_instance();
1452         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
1454         // No feedback should be available because this student has not been graded.
1455         $this->setUser($this->students[0]);
1456         $output = $assign->view_student_summary($this->students[0], true);
1457         $this->assertEquals(false, strpos($output, 'Feedback'), 'Do not show feedback if there is no grade');
1458         // Simulate adding a grade.
1459         $this->setUser($this->teachers[0]);
1460         $data = new stdClass();
1461         $data->grade = '50.0';
1462         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
1464         // Now we should see the feedback.
1465         $this->setUser($this->students[0]);
1466         $output = $assign->view_student_summary($this->students[0], true);
1467         $this->assertNotEquals(false, strpos($output, 'Feedback'), 'Show feedback if there is a grade');
1469         // Now hide the grade in gradebook.
1470         $this->setUser($this->teachers[0]);
1471         require_once($CFG->libdir.'/gradelib.php');
1472         $gradeitem = new grade_item(array(
1473             'itemtype'      => 'mod',
1474             'itemmodule'    => 'assign',
1475             'iteminstance'  => $assign->get_instance()->id,
1476             'courseid'      => $this->course->id));
1478         $gradeitem->set_hidden(1, false);
1480         // No feedback should be available because the grade is hidden.
1481         $this->setUser($this->students[0]);
1482         $output = $assign->view_student_summary($this->students[0], true);
1483         $this->assertEquals(false, strpos($output, 'Feedback'), 'Do not show feedback if the grade is hidden in the gradebook');
1485         // Do the same but add feedback.
1486         $assign = $this->create_instance(array('assignfeedback_comments_enabled' => 1));
1488         $this->setUser($this->teachers[0]);
1489         $grade = $assign->get_user_grade($this->students[0]->id, true);
1490         $data = new stdClass();
1491         $data->assignfeedbackcomments_editor = array('text'=>'Tomato sauce',
1492                                          'format'=>FORMAT_MOODLE);
1493         $plugin = $assign->get_feedback_plugin_by_type('comments');
1494         $plugin->save($grade, $data);
1496         // Should have feedback but no grade.
1497         $this->setUser($this->students[0]);
1498         $output = $assign->view_student_summary($this->students[0], true);
1499         $this->assertNotEquals(false, strpos($output, 'Feedback'), 'Show feedback even if there is no grade');
1500         $this->assertEquals(false, strpos($output, 'Grade'), 'Do not show grade when there is no grade.');
1501         $this->assertEquals(false, strpos($output, 'Graded on'), 'Do not show graded date when there is no grade.');
1503         // Now hide the grade in gradebook.
1504         $this->setUser($this->teachers[0]);
1505         $gradeitem = new grade_item(array(
1506             'itemtype'      => 'mod',
1507             'itemmodule'    => 'assign',
1508             'iteminstance'  => $assign->get_instance()->id,
1509             'courseid'      => $this->course->id));
1511         $gradeitem->set_hidden(1, false);
1513         // No feedback should be available because the grade is hidden.
1514         $this->setUser($this->students[0]);
1515         $output = $assign->view_student_summary($this->students[0], true);
1516         $this->assertEquals(false, strpos($output, 'Feedback'), 'Do not show feedback if the grade is hidden in the gradebook');
1517     }
1519     public function test_attempt_reopen_method_manual() {
1520         global $PAGE;
1522         $this->setUser($this->editingteachers[0]);
1523         $assign = $this->create_instance(array('attemptreopenmethod'=>ASSIGN_ATTEMPT_REOPEN_METHOD_MANUAL,
1524                                                'maxattempts'=>3,
1525                                                'submissiondrafts'=>1,
1526                                                'assignsubmission_onlinetext_enabled'=>1));
1527         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
1529         // Student should be able to see an add submission button.
1530         $this->setUser($this->students[0]);
1531         $output = $assign->view_student_summary($this->students[0], true);
1532         $this->assertNotEquals(false, strpos($output, get_string('addsubmission', 'assign')));
1534         // Add a submission.
1535         $now = time();
1536         $submission = $assign->get_user_submission($this->students[0]->id, true);
1537         $data = new stdClass();
1538         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
1539                                          'text'=>'Submission text',
1540                                          'format'=>FORMAT_MOODLE);
1541         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
1542         $plugin->save($submission, $data);
1544         // And now submit it for marking.
1545         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
1546         $assign->testable_update_submission($submission, $this->students[0]->id, true, false);
1548         // Verify the student cannot make changes to the submission.
1549         $output = $assign->view_student_summary($this->students[0], true);
1550         $this->assertEquals(false, strpos($output, get_string('addsubmission', 'assign')));
1552         // Mark the submission.
1553         $this->setUser($this->teachers[0]);
1554         $data = new stdClass();
1555         $data->grade = '50.0';
1556         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
1558         // Check the student can see the grade.
1559         $this->setUser($this->students[0]);
1560         $output = $assign->view_student_summary($this->students[0], true);
1561         $this->assertNotEquals(false, strpos($output, '50.0'));
1563         // Allow the student another attempt.
1564         $this->teachers[0]->ignoresesskey = true;
1565         $this->setUser($this->teachers[0]);
1566         $result = $assign->testable_process_add_attempt($this->students[0]->id);
1567         $this->assertEquals(true, $result);
1569         // Check that the previous attempt is now in the submission history table.
1570         $this->setUser($this->students[0]);
1571         $output = $assign->view_student_summary($this->students[0], true);
1572         // Need a better check.
1573         $this->assertNotEquals(false, strpos($output, 'Submission text'), 'Contains: Submission text');
1575         // Check that the student now has a button for Add a new attempt".
1576         $this->assertNotEquals(false, strpos($output, get_string('addnewattempt', 'assign')));
1577         // Check that the student now does not have a button for Submit.
1578         $this->assertEquals(false, strpos($output, get_string('submitassignment', 'assign')));
1580         // Check that the student now has a submission history.
1581         $this->assertNotEquals(false, strpos($output, get_string('attempthistory', 'assign')));
1583         $this->setUser($this->teachers[0]);
1584         // Check that the grading table loads correctly and contains this user.
1585         // This is also testing that we do not get duplicate rows in the grading table.
1586         $gradingtable = new assign_grading_table($assign, 100, '', 0, true);
1587         $output = $assign->get_renderer()->render($gradingtable);
1588         $this->assertEquals(true, strpos($output, $this->students[0]->lastname));
1590         // Should be 1 not 2.
1591         $this->assertEquals(1, $assign->count_submissions());
1592         $this->assertEquals(1, $assign->count_submissions_with_status('reopened'));
1593         $this->assertEquals(0, $assign->count_submissions_need_grading());
1594         $this->assertEquals(1, $assign->count_grades());
1596         // Change max attempts to unlimited.
1597         $formdata = clone($assign->get_instance());
1598         $formdata->maxattempts = ASSIGN_UNLIMITED_ATTEMPTS;
1599         $formdata->instance = $formdata->id;
1600         $assign->update_instance($formdata);
1602         // Mark the submission again.
1603         $data = new stdClass();
1604         $data->grade = '60.0';
1605         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 1);
1607         // Check the grade exists.
1608         $grades = $assign->get_user_grades_for_gradebook($this->students[0]->id);
1609         $this->assertEquals(60, (int)$grades[$this->students[0]->id]->rawgrade);
1611         // Check we can reopen still.
1612         $result = $assign->testable_process_add_attempt($this->students[0]->id);
1613         $this->assertEquals(true, $result);
1615         // Should no longer have a grade because there is no grade for the latest attempt.
1616         $grades = $assign->get_user_grades_for_gradebook($this->students[0]->id);
1617         $this->assertEmpty($grades);
1619     }
1621     /**
1622      * Test reopen behavior when in "Reopen until pass" mode.
1623      */
1624     public function test_attempt_reopen_method_untilpass() {
1625         global $PAGE;
1627         $this->setUser($this->editingteachers[0]);
1628         $assign = $this->create_instance(array('attemptreopenmethod' => ASSIGN_ATTEMPT_REOPEN_METHOD_UNTILPASS,
1629                 'maxattempts' => 3,
1630                 'submissiondrafts' => 1,
1631                 'assignsubmission_onlinetext_enabled' => 1));
1632         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
1634         // Set grade to pass to 80.
1635         $gradeitem = $assign->get_grade_item();
1636         $gradeitem->gradepass = '80.0';
1637         $gradeitem->update();
1639         // Student should be able to see an add submission button.
1640         $this->setUser($this->students[0]);
1641         $output = $assign->view_student_summary($this->students[0], true);
1642         $this->assertNotEquals(false, strpos($output, get_string('addsubmission', 'assign')));
1644         // Add a submission.
1645         $now = time();
1646         $submission = $assign->get_user_submission($this->students[0]->id, true);
1647         $data = new stdClass();
1648         $data->onlinetext_editor = array('itemid' => file_get_unused_draft_itemid(),
1649                 'text' => 'Submission text',
1650                 'format' => FORMAT_MOODLE);
1651         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
1652         $plugin->save($submission, $data);
1654         // And now submit it for marking.
1655         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
1656         $assign->testable_update_submission($submission, $this->students[0]->id, true, false);
1658         // Verify the student cannot make a new attempt.
1659         $output = $assign->view_student_summary($this->students[0], true);
1660         $this->assertEquals(false, strpos($output, get_string('addnewattempt', 'assign')));
1662         // Mark the submission as non-passing.
1663         $this->setUser($this->teachers[0]);
1664         $data = new stdClass();
1665         $data->grade = '50.0';
1666         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
1668         // Check the student can see the grade.
1669         $this->setUser($this->students[0]);
1670         $output = $assign->view_student_summary($this->students[0], true);
1671         $this->assertNotEquals(false, strpos($output, '50.0'));
1673         // Check that the student now has a button for Add a new attempt.
1674         $output = $assign->view_student_summary($this->students[0], true);
1675         $this->assertNotEquals(false, strpos($output, get_string('addnewattempt', 'assign')));
1677         // Check that the student now does not have a button for Submit.
1678         $this->assertEquals(false, strpos($output, get_string('submitassignment', 'assign')));
1680         // Check that the student now has a submission history.
1681         $this->assertNotEquals(false, strpos($output, get_string('attempthistory', 'assign')));
1683         // Add a second submission.
1684         $now = time();
1685         $submission = $assign->get_user_submission($this->students[0]->id, true, 1);
1686         $data = new stdClass();
1687         $data->onlinetext_editor = array('itemid' => file_get_unused_draft_itemid(),
1688                 'text' => 'Submission text',
1689                 'format' => FORMAT_MOODLE);
1690         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
1691         $plugin->save($submission, $data);
1693         // And now submit it for marking.
1694         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
1695         $assign->testable_update_submission($submission, $this->students[0]->id, true, false);
1697         // Mark the submission as passing.
1698         $this->setUser($this->teachers[0]);
1699         $data = new stdClass();
1700         $data->grade = '80.0';
1701         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 1);
1703         // Check that the student does not have a button for Add a new attempt.
1704         $this->setUser($this->students[0]);
1705         $output = $assign->view_student_summary($this->students[0], true);
1706         $this->assertEquals(false, strpos($output, get_string('addnewattempt', 'assign')));
1708         // Re-mark the submission as not passing.
1709         $this->setUser($this->teachers[0]);
1710         $data = new stdClass();
1711         $data->grade = '50.0';
1712         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 1);
1714         // Check that the student now has a button for Add a new attempt.
1715         $this->setUser($this->students[0]);
1716         $output = $assign->view_student_summary($this->students[0], true);
1717         $this->assertNotEquals(false, strpos($output, get_string('addnewattempt', 'assign')));
1719         // Add a submission as a second student.
1720         $this->setUser($this->students[1]);
1721         $now = time();
1722         $submission = $assign->get_user_submission($this->students[1]->id, true);
1723         $data = new stdClass();
1724         $data->onlinetext_editor = array('itemid' => file_get_unused_draft_itemid(),
1725                 'text' => 'Submission text',
1726                 'format' => FORMAT_MOODLE);
1727         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
1728         $plugin->save($submission, $data);
1730         // And now submit it for marking.
1731         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
1732         $assign->testable_update_submission($submission, $this->students[1]->id, true, false);
1734         // Mark the submission as passing.
1735         $this->setUser($this->teachers[0]);
1736         $data = new stdClass();
1737         $data->grade = '100.0';
1738         $assign->testable_apply_grade_to_user($data, $this->students[1]->id, 0);
1740         // Check the student can see the grade.
1741         $this->setUser($this->students[1]);
1742         $output = $assign->view_student_summary($this->students[1], true);
1743         $this->assertNotEquals(false, strpos($output, '100.0'));
1745         // Check that the student does not have a button for Add a new attempt.
1746         $output = $assign->view_student_summary($this->students[1], true);
1747         $this->assertEquals(false, strpos($output, get_string('addnewattempt', 'assign')));
1749         // Set grade to pass to 0, so that no attempts should reopen.
1750         $gradeitem = $assign->get_grade_item();
1751         $gradeitem->gradepass = '0';
1752         $gradeitem->update();
1754         // Add another submission.
1755         $this->setUser($this->students[2]);
1756         $now = time();
1757         $submission = $assign->get_user_submission($this->students[2]->id, true);
1758         $data = new stdClass();
1759         $data->onlinetext_editor = array('itemid' => file_get_unused_draft_itemid(),
1760                 'text' => 'Submission text',
1761                 'format' => FORMAT_MOODLE);
1762         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
1763         $plugin->save($submission, $data);
1765         // And now submit it for marking.
1766         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
1767         $assign->testable_update_submission($submission, $this->students[2]->id, true, false);
1769         // Mark the submission as graded.
1770         $this->setUser($this->teachers[0]);
1771         $data = new stdClass();
1772         $data->grade = '0.0';
1773         $assign->testable_apply_grade_to_user($data, $this->students[2]->id, 0);
1775         // Check the student can see the grade.
1776         $this->setUser($this->students[2]);
1777         $output = $assign->view_student_summary($this->students[2], true);
1778         $this->assertNotEquals(false, strpos($output, '0.0'));
1780         // Check that the student does not have a button for Add a new attempt.
1781         $output = $assign->view_student_summary($this->students[2], true);
1782         $this->assertEquals(false, strpos($output, get_string('addnewattempt', 'assign')));
1783     }
1786     public function test_markingworkflow() {
1787         global $PAGE;
1789         $this->setUser($this->editingteachers[0]);
1790         $assign = $this->create_instance(array('markingworkflow'=>1));
1791         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
1793         // Mark the submission and set to notmarked.
1794         $this->setUser($this->teachers[0]);
1795         $data = new stdClass();
1796         $data->grade = '50.0';
1797         $data->workflowstate = ASSIGN_MARKING_WORKFLOW_STATE_NOTMARKED;
1798         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
1800         // Check the student can't see the grade.
1801         $this->setUser($this->students[0]);
1802         $output = $assign->view_student_summary($this->students[0], true);
1803         $this->assertEquals(false, strpos($output, '50.0'));
1805         // Make sure the grade isn't pushed to the gradebook.
1806         $grades = $assign->get_user_grades_for_gradebook($this->students[0]->id);
1807         $this->assertEmpty($grades);
1809         // Mark the submission and set to inmarking.
1810         $this->setUser($this->teachers[0]);
1811         $data = new stdClass();
1812         $data->grade = '50.0';
1813         $data->workflowstate = ASSIGN_MARKING_WORKFLOW_STATE_INMARKING;
1814         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
1816         // Check the student can't see the grade.
1817         $this->setUser($this->students[0]);
1818         $output = $assign->view_student_summary($this->students[0], true);
1819         $this->assertEquals(false, strpos($output, '50.0'));
1821         // Make sure the grade isn't pushed to the gradebook.
1822         $grades = $assign->get_user_grades_for_gradebook($this->students[0]->id);
1823         $this->assertEmpty($grades);
1825         // Mark the submission and set to readyforreview.
1826         $this->setUser($this->teachers[0]);
1827         $data = new stdClass();
1828         $data->grade = '50.0';
1829         $data->workflowstate = ASSIGN_MARKING_WORKFLOW_STATE_READYFORREVIEW;
1830         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
1832         // Check the student can't see the grade.
1833         $this->setUser($this->students[0]);
1834         $output = $assign->view_student_summary($this->students[0], true);
1835         $this->assertEquals(false, strpos($output, '50.0'));
1837         // Make sure the grade isn't pushed to the gradebook.
1838         $grades = $assign->get_user_grades_for_gradebook($this->students[0]->id);
1839         $this->assertEmpty($grades);
1841         // Mark the submission and set to inreview.
1842         $this->setUser($this->teachers[0]);
1843         $data = new stdClass();
1844         $data->grade = '50.0';
1845         $data->workflowstate = ASSIGN_MARKING_WORKFLOW_STATE_INREVIEW;
1846         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
1848         // Check the student can't see the grade.
1849         $this->setUser($this->students[0]);
1850         $output = $assign->view_student_summary($this->students[0], true);
1851         $this->assertEquals(false, strpos($output, '50.0'));
1853         // Make sure the grade isn't pushed to the gradebook.
1854         $grades = $assign->get_user_grades_for_gradebook($this->students[0]->id);
1855         $this->assertEmpty($grades);
1857         // Mark the submission and set to readyforrelease.
1858         $this->setUser($this->teachers[0]);
1859         $data = new stdClass();
1860         $data->grade = '50.0';
1861         $data->workflowstate = ASSIGN_MARKING_WORKFLOW_STATE_READYFORRELEASE;
1862         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
1864         // Check the student can't see the grade.
1865         $this->setUser($this->students[0]);
1866         $output = $assign->view_student_summary($this->students[0], true);
1867         $this->assertEquals(false, strpos($output, '50.0'));
1869         // Make sure the grade isn't pushed to the gradebook.
1870         $grades = $assign->get_user_grades_for_gradebook($this->students[0]->id);
1871         $this->assertEmpty($grades);
1873         // Mark the submission and set to released.
1874         $this->setUser($this->teachers[0]);
1875         $data = new stdClass();
1876         $data->grade = '50.0';
1877         $data->workflowstate = ASSIGN_MARKING_WORKFLOW_STATE_RELEASED;
1878         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
1880         // Check the student can see the grade.
1881         $this->setUser($this->students[0]);
1882         $output = $assign->view_student_summary($this->students[0], true);
1883         $this->assertNotEquals(false, strpos($output, '50.0'));
1885         // Make sure the grade is pushed to the gradebook.
1886         $grades = $assign->get_user_grades_for_gradebook($this->students[0]->id);
1887         $this->assertEquals(50, (int)$grades[$this->students[0]->id]->rawgrade);
1888     }
1890     public function test_markerallocation() {
1891         global $PAGE;
1893         $this->setUser($this->editingteachers[0]);
1894         $assign = $this->create_instance(array('markingworkflow'=>1, 'markingallocation'=>1));
1895         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
1897         // Allocate marker to submission.
1898         $data = new stdClass();
1899         $data->allocatedmarker = $this->teachers[0]->id;
1900         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
1902         // Check the allocated marker can view the submission.
1903         $this->setUser($this->teachers[0]);
1904         $gradingtable = new assign_grading_table($assign, 100, '', 0, true);
1905         $output = $assign->get_renderer()->render($gradingtable);
1906         $this->assertEquals(true, strpos($output, $this->students[0]->lastname));
1908         // Check that other teachers can't view this submission.
1909         $this->setUser($this->teachers[1]);
1910         $gradingtable = new assign_grading_table($assign, 100, '', 0, true);
1911         $output = $assign->get_renderer()->render($gradingtable);
1912         $this->assertNotEquals(true, strpos($output, $this->students[0]->lastname));
1913     }
1917     public function test_teacher_submit_for_student() {
1918         global $PAGE;
1920         $this->preventResetByRollback();
1921         $sink = $this->redirectMessages();
1923         $this->setUser($this->editingteachers[0]);
1925         $assign = $this->create_instance(array('assignsubmission_onlinetext_enabled'=>1, 'submissiondrafts'=>1));
1926         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
1928         $this->setUser($this->students[0]);
1929         // Simulate a submission.
1930         $data = new stdClass();
1931         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
1932                                          'text'=>'Student submission text',
1933                                          'format'=>FORMAT_MOODLE);
1935         $notices = array();
1936         $assign->save_submission($data, $notices);
1938         // Check that the submission text was saved.
1939         $output = $assign->view_student_summary($this->students[0], true);
1940         $this->assertContains('Student submission text', $output, 'Contains student submission text');
1942         // Check that a teacher teacher with the extra capability can edit a students submission.
1943         $this->setUser($this->teachers[0]);
1944         $data = new stdClass();
1945         $data->userid = $this->students[0]->id;
1946         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
1947                                          'text'=>'Teacher edited submission text',
1948                                          'format'=>FORMAT_MOODLE);
1950         // Add the required capability.
1951         $roleid = create_role('Dummy role', 'dummyrole', 'dummy role description');
1952         assign_capability('mod/assign:editothersubmission', CAP_ALLOW, $roleid, $assign->get_context()->id);
1953         role_assign($roleid, $this->teachers[0]->id, $assign->get_context()->id);
1954         accesslib_clear_all_caches_for_unit_testing();
1956         // Try to save the submission.
1957         $notices = array();
1958         $assign->save_submission($data, $notices);
1960         // Check that the teacher can submit the students work.
1961         $data = new stdClass();
1962         $data->userid = $this->students[0]->id;
1963         $notices = array();
1964         $assign->submit_for_grading($data, $notices);
1966         // Revert to draft so the student can edit it.
1967         $assign->revert_to_draft($this->students[0]->id);
1969         $this->setUser($this->students[0]);
1971         // Check that the submission text was saved.
1972         $output = $assign->view_student_summary($this->students[0], true);
1973         $this->assertContains('Teacher edited submission text', $output, 'Contains student submission text');
1975         // Check that the student can submit their work.
1976         $data = new stdClass();
1977         $assign->submit_for_grading($data, $notices);
1979         $output = $assign->view_student_summary($this->students[0], true);
1980         $this->assertNotContains(get_string('addsubmission', 'assign'), $output);
1982         // Set to a default editing teacher who should not be able to edit this submission.
1983         $this->setUser($this->editingteachers[1]);
1985         // Revert to draft so the submission is editable.
1986         $assign->revert_to_draft($this->students[0]->id);
1988         $data = new stdClass();
1989         $data->userid = $this->students[0]->id;
1990         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
1991                                          'text'=>'Teacher 2 edited submission text',
1992                                          'format'=>FORMAT_MOODLE);
1994         $notices = array();
1995         $this->setExpectedException('moodle_exception');
1996         $assign->save_submission($data, $notices);
1998         $sink->close();
1999     }
2001     public function test_disable_submit_after_cutoff_date() {
2002         global $PAGE;
2004         $this->setUser($this->editingteachers[0]);
2005         $now = time();
2006         $tomorrow = $now + 24*60*60;
2007         $lastweek = $now - 7*24*60*60;
2008         $yesterday = $now - 24*60*60;
2010         $assign = $this->create_instance(array('duedate'=>$yesterday,
2011                                                'cutoffdate'=>$tomorrow,
2012                                                'assignsubmission_onlinetext_enabled'=>1));
2013         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
2015         // Student should be able to see an add submission button.
2016         $this->setUser($this->students[0]);
2017         $output = $assign->view_student_summary($this->students[0], true);
2018         $this->assertNotEquals(false, strpos($output, get_string('addsubmission', 'assign')));
2020         // Add a submission but don't submit now.
2021         $submission = $assign->get_user_submission($this->students[0]->id, true);
2022         $data = new stdClass();
2023         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
2024                                          'text'=>'Submission text',
2025                                          'format'=>FORMAT_MOODLE);
2026         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
2027         $plugin->save($submission, $data);
2029         // Create another instance with cut-off and due-date already passed.
2030         $this->setUser($this->editingteachers[0]);
2031         $now = time();
2032         $assign = $this->create_instance(array('duedate'=>$lastweek,
2033                                                'cutoffdate'=>$yesterday,
2034                                                'assignsubmission_onlinetext_enabled'=>1));
2036         $this->setUser($this->students[0]);
2037         $output = $assign->view_student_summary($this->students[0], true);
2038         $this->assertNotContains($output, get_string('editsubmission', 'assign'),
2039                                  'Should not be able to edit after cutoff date.');
2040         $this->assertNotContains($output, get_string('submitassignment', 'assign'),
2041                                  'Should not be able to submit after cutoff date.');
2042     }
2043     /**
2044      * Testing for submission comment plugin settings
2045      */
2046     public function test_submission_comment_plugin_settings() {
2047         global $CFG;
2049         $commentconfig = false;
2050         if (!empty($CFG->usecomments)) {
2051             $commentconfig = $CFG->usecomments;
2052         }
2054         $CFG->usecomments = true;
2055         $assign = $this->create_instance();
2056         $plugin = $assign->get_submission_plugin_by_type('comments');
2057         $this->assertEquals(1, $plugin->is_enabled('enabled'));
2059         $assign = $this->create_instance(array('assignsubmission_comments_enabled' => 0));
2060         $plugin = $assign->get_submission_plugin_by_type('comments');
2061         $this->assertEquals(1, $plugin->is_enabled('enabled'));
2063         $assign = $this->create_instance(array('assignsubmission_comments_enabled' => 1));
2064         $plugin = $assign->get_submission_plugin_by_type('comments');
2065         $this->assertEquals(1, $plugin->is_enabled('enabled'));
2067         $CFG->usecomments = false;
2068         $assign = $this->create_instance();
2069         $plugin = $assign->get_submission_plugin_by_type('comments');
2070         $this->assertEquals(0, $plugin->is_enabled('enabled'));
2072         $assign = $this->create_instance(array('assignsubmission_comments_enabled' => 0));
2073         $plugin = $assign->get_submission_plugin_by_type('comments');
2074         $this->assertEquals(0, $plugin->is_enabled('enabled'));
2076         $assign = $this->create_instance(array('assignsubmission_comments_enabled' => 1));
2077         $plugin = $assign->get_submission_plugin_by_type('comments');
2078         $this->assertEquals(0, $plugin->is_enabled('enabled'));
2080         $CFG->usecomments = $commentconfig;
2081     }
2083     /**
2084      * Testing for comment inline settings
2085      */
2086     public function test_feedback_comment_commentinline() {
2087         global $CFG;
2089         $sourcetext = "Hello!
2091 I'm writing to you from the Moodle Majlis in Muscat, Oman, where we just had several days of Moodle community goodness.
2093 URL outside a tag: https://moodle.org/logo/logo-240x60.gif
2094 Plugin url outside a tag: @@PLUGINFILE@@/logo-240x60.gif
2096 External link 1:<img src='https://moodle.org/logo/logo-240x60.gif' alt='Moodle'/>
2097 External link 2:<img alt=\"Moodle\" src=\"https://moodle.org/logo/logo-240x60.gif\"/>
2098 Internal link 1:<img src='@@PLUGINFILE@@/logo-240x60.gif' alt='Moodle'/>
2099 Internal link 2:<img alt=\"Moodle\" src=\"@@PLUGINFILE@@logo-240x60.gif\"/>
2100 Anchor link 1:<a href=\"@@PLUGINFILE@@logo-240x60.gif\" alt=\"bananas\">Link text</a>
2101 Anchor link 2:<a title=\"bananas\" href=\"../logo-240x60.gif\">Link text</a>
2102 ";
2104         // Note the internal images have been stripped and the html is purified (quotes fixed in this case).
2105         $filteredtext = "Hello!
2107 I'm writing to you from the Moodle Majlis in Muscat, Oman, where we just had several days of Moodle community goodness.
2109 URL outside a tag: https://moodle.org/logo/logo-240x60.gif
2110 Plugin url outside a tag: @@PLUGINFILE@@/logo-240x60.gif
2112 External link 1:<img src=\"https://moodle.org/logo/logo-240x60.gif\" alt=\"Moodle\" />
2113 External link 2:<img alt=\"Moodle\" src=\"https://moodle.org/logo/logo-240x60.gif\" />
2114 Internal link 1:
2115 Internal link 2:
2116 Anchor link 1:Link text
2117 Anchor link 2:<a title=\"bananas\" href=\"../logo-240x60.gif\">Link text</a>
2118 ";
2120         $this->setUser($this->editingteachers[0]);
2121         $params = array('assignsubmission_onlinetext_enabled' => 1,
2122                         'assignfeedback_comments_enabled' => 1,
2123                         'assignfeedback_comments_commentinline' => 1);
2124         $assign = $this->create_instance($params);
2126         $this->setUser($this->students[0]);
2127         // Add a submission but don't submit now.
2128         $submission = $assign->get_user_submission($this->students[0]->id, true);
2129         $data = new stdClass();
2131         // Test the internal link is stripped, but the external one is not.
2132         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
2133                                          'text'=>$sourcetext,
2134                                          'format'=>FORMAT_MOODLE);
2136         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
2137         $plugin->save($submission, $data);
2139         $this->setUser($this->editingteachers[0]);
2141         $data = new stdClass();
2142         require_once($CFG->dirroot . '/mod/assign/gradeform.php');
2143         $pagination = array('userid'=>$this->students[0]->id,
2144                             'rownum'=>0,
2145                             'last'=>true,
2146                             'useridlistid' => $assign->get_useridlist_key_id(),
2147                             'attemptnumber'=>0);
2148         $formparams = array($assign, $data, $pagination);
2149         $mform = new mod_assign_grade_form(null, $formparams);
2151         $this->assertEquals($filteredtext, $data->assignfeedbackcomments_editor['text']);
2152     }
2154     /**
2155      * Testing for feedback comment plugin settings
2156      */
2157     public function test_feedback_plugin_settings() {
2159         $assign = $this->create_instance();
2160         $plugin = $assign->get_feedback_plugin_by_type('comments');
2161         $this->assertEquals(0, $plugin->is_enabled('enabled'));
2163         $assign = $this->create_instance(array('assignfeedback_comments_enabled' => 0));
2164         $plugin = $assign->get_feedback_plugin_by_type('comments');
2165         $this->assertEquals(0, $plugin->is_enabled('enabled'));
2167         $assign = $this->create_instance(array('assignfeedback_comments_enabled' => 1));
2168         $plugin = $assign->get_feedback_plugin_by_type('comments');
2169         $this->assertEquals(1, $plugin->is_enabled('enabled'));
2170     }
2172     /**
2173      * Testing if gradebook feedback plugin is enabled.
2174      */
2175     public function test_is_gradebook_feedback_enabled() {
2176         $adminconfig = get_config('assign');
2177         $gradebookplugin = $adminconfig->feedback_plugin_for_gradebook;
2179         // Create assignment with gradebook feedback enabled and grade = 0.
2180         $assign = $this->create_instance(array($gradebookplugin . '_enabled' => 1, 'grades' => 0));
2182         // Get gradebook feedback plugin.
2183         $gradebookplugintype = str_replace('assignfeedback_', '', $gradebookplugin);
2184         $plugin = $assign->get_feedback_plugin_by_type($gradebookplugintype);
2185         $this->assertEquals(1, $plugin->is_enabled('enabled'));
2186         $this->assertEquals(1, $assign->is_gradebook_feedback_enabled());
2188         // Create assignment with gradebook feedback disabled and grade = 0.
2189         $assign = $this->create_instance(array($gradebookplugin . '_enabled' => 0, 'grades' => 0));
2190         $plugin = $assign->get_feedback_plugin_by_type($gradebookplugintype);
2191         $this->assertEquals(0, $plugin->is_enabled('enabled'));
2192     }
2194     /**
2195      * Testing can_edit_submission
2196      */
2197     public function test_can_edit_submission() {
2198         global $PAGE, $DB;
2199         $this->create_extra_users();
2201         $this->setAdminUser();
2202         // Create assignment (onlinetext).
2203         $assign = $this->create_instance(array('assignsubmission_onlinetext_enabled'=>1, 'submissiondrafts'=>1));
2204         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
2206         // Check student can edit their own submission.
2207         $this->assertTrue($assign->can_edit_submission($this->students[0]->id, $this->students[0]->id));
2208         // Check student cannot edit others submission.
2209         $this->assertFalse($assign->can_edit_submission($this->students[0]->id, $this->students[1]->id));
2211         // Check teacher cannot (by default) edit a students submission.
2212         $this->assertFalse($assign->can_edit_submission($this->students[0]->id, $this->teachers[0]->id));
2214         // Add the required capability to edit a student submission.
2215         $roleid = create_role('Dummy role', 'dummyrole', 'dummy role description');
2216         assign_capability('mod/assign:editothersubmission', CAP_ALLOW, $roleid, $assign->get_context()->id);
2217         role_assign($roleid, $this->teachers[0]->id, $assign->get_context()->id);
2218         accesslib_clear_all_caches_for_unit_testing();
2219         // Retest - should now have access.
2220         $this->assertTrue($assign->can_edit_submission($this->students[0]->id, $this->teachers[0]->id));
2222         // Force create an assignment with SEPARATEGROUPS.
2223         $data = new stdClass();
2224         $data->courseid = $this->course->id;
2225         $data->name = 'Grouping';
2226         $groupingid = groups_create_grouping($data);
2227         groups_assign_grouping($groupingid, $this->groups[0]->id);
2228         groups_assign_grouping($groupingid, $this->groups[1]->id);
2229         $assign = $this->create_instance(array('groupingid' => $groupingid, 'groupmode' => SEPARATEGROUPS));
2231         // Add the capability to the new assignment for extra students 0 and 1.
2232         assign_capability('mod/assign:editothersubmission', CAP_ALLOW, $roleid, $assign->get_context()->id);
2233         role_assign($roleid, $this->extrastudents[0]->id, $assign->get_context()->id);
2234         role_assign($roleid, $this->extrastudents[1]->id, $assign->get_context()->id);
2235         accesslib_clear_all_caches_for_unit_testing();
2237         // Verify the extra student does not have the capability to edit a submission not in their group.
2238         $this->assertFalse($assign->can_edit_submission($this->students[0]->id, $this->extrastudents[1]->id));
2239         // Verify the extra student does have the capability to edit a submission in their group.
2240         $this->assertTrue($assign->can_edit_submission($this->students[0]->id, $this->extrastudents[0]->id));
2242     }
2244     /**
2245      * Test if the view blind details capability works
2246      */
2247     public function test_can_view_blind_details() {
2248         global $PAGE, $DB;
2249         $teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
2250         $managerrole = $DB->get_record('role', array('shortname' => 'manager'));
2252         $student = $this->students[0];// Get a student user.
2253         // Create a teacher. Shouldn't be able to view blind marking ID.
2254         $teacher = $this->getDataGenerator()->create_user();
2256         $this->getDataGenerator()->enrol_user($teacher->id,
2257                                               $this->course->id,
2258                                               $teacherrole->id);
2260         // Create a manager.. Should be able to view blind marking ID.
2261         $manager = $this->getDataGenerator()->create_user();
2262         $this->getDataGenerator()->enrol_user($manager->id,
2263                 $this->course->id,
2264                 $managerrole->id);
2266         // Generate blind marking assignment.
2267         $assign = $this->create_instance(array('blindmarking' => 1));
2268         $this->assertEquals(true, $assign->is_blind_marking());
2270         // Test student names are hidden to teacher.
2271         $this->setUser($teacher);
2272         $gradingtable = new assign_grading_table($assign, 1, '', 0, true);
2273         $output = $assign->get_renderer()->render($gradingtable);
2274         $this->assertEquals(true, strpos($output, get_string('hiddenuser', 'assign')));    // "Participant" is somewhere on the page.
2275         $this->assertEquals(false, strpos($output, fullname($student)));    // Students full name doesn't appear.
2277         // Test student names are visible to manager.
2278         $this->setUser($manager);
2279         $gradingtable = new assign_grading_table($assign, 1, '', 0, true);
2280         $output = $assign->get_renderer()->render($gradingtable);
2281         $this->assertEquals(true, strpos($output, get_string('hiddenuser', 'assign')));
2282         $this->assertEquals(true, strpos($output, fullname($student)));
2283     }
2285     /**
2286      * Testing get_shared_group_members
2287      */
2288     public function test_get_shared_group_members() {
2289         $this->create_extra_users();
2290         $this->setAdminUser();
2292         // Force create an assignment with SEPARATEGROUPS.
2293         $data = new stdClass();
2294         $data->courseid = $this->course->id;
2295         $data->name = 'Grouping';
2296         $groupingid = groups_create_grouping($data);
2297         groups_assign_grouping($groupingid, $this->groups[0]->id);
2298         groups_assign_grouping($groupingid, $this->groups[1]->id);
2299         $assign = $this->create_instance(array('groupingid' => $groupingid, 'groupmode' => SEPARATEGROUPS));
2300         $cm = $assign->get_course_module();
2302         // Add the capability to access allgroups.
2303         $roleid = create_role('Access all groups role', 'accessallgroupsrole', '');
2304         assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $roleid, $assign->get_context()->id);
2305         role_assign($roleid, $this->extrastudents[3]->id, $assign->get_context()->id);
2306         accesslib_clear_all_caches_for_unit_testing();
2308         // Get shared group members for students 0 and 1.
2309         $groupmembers = array();
2310         $groupmembers[0] = $assign->get_shared_group_members($cm, $this->students[0]->id);
2311         $groupmembers[1] = $assign->get_shared_group_members($cm, $this->students[1]->id);
2313         // They should share groups with extrastudents 0 and 1.
2314         $this->assertTrue(in_array($this->extrastudents[0]->id, $groupmembers[0]));
2315         $this->assertFalse(in_array($this->extrastudents[0]->id, $groupmembers[1]));
2316         $this->assertTrue(in_array($this->extrastudents[1]->id, $groupmembers[1]));
2317         $this->assertFalse(in_array($this->extrastudents[1]->id, $groupmembers[0]));
2319         // Lists of group members for students and extrastudents should be the same.
2320         $this->assertEquals($groupmembers[0], $assign->get_shared_group_members($cm, $this->extrastudents[0]->id));
2321         $this->assertEquals($groupmembers[1], $assign->get_shared_group_members($cm, $this->extrastudents[1]->id));
2323         // Get all group members for extrastudent 3 wich can access all groups.
2324         $allgroupmembers = $assign->get_shared_group_members($cm, $this->extrastudents[3]->id);
2326         // Extrastudent 3 should see students 0 and 1, extrastudent 0 and 1.
2327         $this->assertTrue(in_array($this->students[0]->id, $allgroupmembers));
2328         $this->assertTrue(in_array($this->students[1]->id, $allgroupmembers));
2329         $this->assertTrue(in_array($this->extrastudents[0]->id, $allgroupmembers));
2330         $this->assertTrue(in_array($this->extrastudents[1]->id , $allgroupmembers));
2331     }
2333     /**
2334      * Test the quicksave grades processor
2335      */
2336     public function test_process_save_quick_grades() {
2337         $this->editingteachers[0]->ignoresesskey = true;
2338         $this->setUser($this->editingteachers[0]);
2340         $assign = $this->create_instance(array('attemptreopenmethod' => ASSIGN_ATTEMPT_REOPEN_METHOD_MANUAL));
2342         // Initially grade the user.
2343         $grade = $assign->get_user_grade($this->students[0]->id, false);
2344         if (!$grade) {
2345             $grade = new stdClass();
2346             $grade->attemptnumber = '';
2347             $grade->timemodified = '';
2348         }
2349         $data = array(
2350             'grademodified_' . $this->students[0]->id => $grade->timemodified,
2351             'gradeattempt_' . $this->students[0]->id => $grade->attemptnumber,
2352             'quickgrade_' . $this->students[0]->id => '60.0'
2353         );
2354         $result = $assign->testable_process_save_quick_grades($data);
2355         $this->assertContains(get_string('quickgradingchangessaved', 'assign'), $result);
2356         $grade = $assign->get_user_grade($this->students[0]->id, false);
2357         $this->assertEquals('60.0', $grade->grade);
2359         // Attempt to grade with a past attempts grade info.
2360         $assign->testable_process_add_attempt($this->students[0]->id);
2361         $data = array(
2362             'grademodified_' . $this->students[0]->id => $grade->timemodified,
2363             'gradeattempt_' . $this->students[0]->id => $grade->attemptnumber,
2364             'quickgrade_' . $this->students[0]->id => '50.0'
2365         );
2366         $result = $assign->testable_process_save_quick_grades($data);
2367         $this->assertContains(get_string('errorrecordmodified', 'assign'), $result);
2368         $grade = $assign->get_user_grade($this->students[0]->id, false);
2369         $this->assertFalse($grade);
2371         // Attempt to grade a the attempt.
2372         $submission = $assign->get_user_submission($this->students[0]->id, false);
2373         $data = array(
2374             'grademodified_' . $this->students[0]->id => '',
2375             'gradeattempt_' . $this->students[0]->id => $submission->attemptnumber,
2376             'quickgrade_' . $this->students[0]->id => '40.0'
2377         );
2378         $result = $assign->testable_process_save_quick_grades($data);
2379         $this->assertContains(get_string('quickgradingchangessaved', 'assign'), $result);
2380         $grade = $assign->get_user_grade($this->students[0]->id, false);
2381         $this->assertEquals('40.0', $grade->grade);
2383         // Catch grade update conflicts.
2384         // Save old data for later.
2385         $pastdata = $data;
2386         // Update the grade the 'good' way.
2387         $data = array(
2388             'grademodified_' . $this->students[0]->id => $grade->timemodified,
2389             'gradeattempt_' . $this->students[0]->id => $grade->attemptnumber,
2390             'quickgrade_' . $this->students[0]->id => '30.0'
2391         );
2392         $result = $assign->testable_process_save_quick_grades($data);
2393         $this->assertContains(get_string('quickgradingchangessaved', 'assign'), $result);
2394         $grade = $assign->get_user_grade($this->students[0]->id, false);
2395         $this->assertEquals('30.0', $grade->grade);
2397         // Now update using 'old' data. Should fail.
2398         $result = $assign->testable_process_save_quick_grades($pastdata);
2399         $this->assertContains(get_string('errorrecordmodified', 'assign'), $result);
2400         $grade = $assign->get_user_grade($this->students[0]->id, false);
2401         $this->assertEquals('30.0', $grade->grade);
2402     }