e2f6ea0c12b0f8eedfd39c56cc65d080aece06b9
[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         $assign = $this->create_instance(array(
183             'assignsubmission_onlinetext_enabled'=>1,
184             'duedate' => time() - 4 * 24 * 60 * 60,
185          ));
186         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array(
187             'id' => $assign->get_course_module()->id,
188             'action' => 'grading',
189         )));
191         // Check that the assignment is late.
192         $gradingtable = new assign_grading_table($assign, 1, '', 0, true);
193         $output = $assign->get_renderer()->render($gradingtable);
194         $this->assertContains(get_string('submissionstatus_', 'assign'), $output);
195         $this->assertContains(get_string('overdue', 'assign', format_time(4*24*60*60)), $output);
197         // Grant an extension that is in the past.
198         $assign->testable_save_user_extension($this->students[0]->id, time() - 2 * 24 * 60 * 60);
199         $gradingtable = new assign_grading_table($assign, 1, '', 0, true);
200         $output = $assign->get_renderer()->render($gradingtable);
201         $this->assertContains(get_string('submissionstatus_', 'assign'), $output);
202         $this->assertContains(get_string('userextensiondate', 'assign', userdate(time() - 2*24*60*60)), $output);
203         $this->assertContains(get_string('overdue', 'assign', format_time(2*24*60*60)), $output);
205         // Simulate a submission.
206         $this->setUser($this->students[0]);
207         $submission = $assign->get_user_submission($this->students[0]->id, true);
208         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
209         $assign->testable_update_submission($submission, $this->students[0]->id, true, false);
210         $data = new stdClass();
211         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
212                                          'text'=>'Submission text',
213                                          'format'=>FORMAT_MOODLE);
214         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
215         $plugin->save($submission, $data);
217         // Verify output.
218         $this->setUser($this->editingteachers[0]);
219         $gradingtable = new assign_grading_table($assign, 1, '', 0, true);
220         $output = $assign->get_renderer()->render($gradingtable);
221         $this->assertContains(get_string('submissionstatus_submitted', 'assign'), $output);
222         $this->assertContains(get_string('userextensiondate', 'assign', userdate(time() - 2*24*60*60)), $output);
223         $this->assertContains(get_string('submittedlateshort', 'assign', format_time(2*24*60*60)), $output);
224     }
226     /**
227      * Check that group submission information is rendered correctly in the
228      * grading table.
229      */
230     public function test_gradingtable_group_submissions_rendering() {
231         global $PAGE;
233         $this->create_extra_users();
234         // Now verify group assignments.
235         $this->setUser($this->teachers[0]);
236         $assign = $this->create_instance(array(
237             'teamsubmission' => 1,
238             'assignsubmission_onlinetext_enabled' => 1,
239             'submissiondrafts' => 1,
240             'requireallteammemberssubmit' => 0,
241         ));
242         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array(
243             'id' => $assign->get_course_module()->id,
244             'action' => 'grading',
245         )));
247         // Add a submission.
248         $this->setUser($this->extrastudents[0]);
249         $data = new stdClass();
250         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
251                                          'text'=>'Submission text',
252                                          'format'=>FORMAT_MOODLE);
253         $notices = array();
254         $assign->save_submission($data, $notices);
256         $submission = $assign->get_group_submission($this->extrastudents[0]->id, 0, true);
257         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
258         $assign->testable_update_submission($submission, $this->extrastudents[0]->id, true, true);
260         // Check output.
261         $this->setUser($this->teachers[0]);
262         $gradingtable = new assign_grading_table($assign, 4, '', 0, true);
263         $output = $assign->get_renderer()->render($gradingtable);
264         $document = new DOMDocument();
265         $document->loadHTML($output);
266         $xpath = new DOMXPath($document);
268         // Check status.
269         $this->assertSame(get_string('submissionstatus_submitted', 'assign'), $xpath->evaluate('string(//td[@id="mod_assign_grading_r0_c4"]/div[@class="submissionstatussubmitted"])'));
270         $this->assertSame(get_string('submissionstatus_submitted', 'assign'), $xpath->evaluate('string(//td[@id="mod_assign_grading_r3_c4"]/div[@class="submissionstatussubmitted"])'));
272         // Check submission last modified date
273         $this->assertGreaterThan(0, strtotime($xpath->evaluate('string(//td[@id="mod_assign_grading_r0_c8"])')));
274         $this->assertGreaterThan(0, strtotime($xpath->evaluate('string(//td[@id="mod_assign_grading_r3_c8"])')));
276         // Check group.
277         $this->assertSame($this->groups[0]->name, $xpath->evaluate('string(//td[@id="mod_assign_grading_r0_c5"])'));
278         $this->assertSame($this->groups[0]->name, $xpath->evaluate('string(//td[@id="mod_assign_grading_r3_c5"])'));
280         // Check submission text.
281         $this->assertSame('Submission text', $xpath->evaluate('string(//td[@id="mod_assign_grading_r0_c9"]/div/div)'));
282         $this->assertSame('Submission text', $xpath->evaluate('string(//td[@id="mod_assign_grading_r3_c9"]/div/div)'));
284         // Check comments can be made.
285         $this->assertSame(1, (int)$xpath->evaluate('count(//td[@id="mod_assign_grading_r0_c10"]//textarea)'));
286         $this->assertSame(1, (int)$xpath->evaluate('count(//td[@id="mod_assign_grading_r3_c10"]//textarea)'));
287     }
289     public function test_show_intro() {
290         // Test whether we are showing the intro at the correct times.
291         $this->setUser($this->editingteachers[0]);
292         $assign = $this->create_instance(array('alwaysshowdescription'=>1));
294         $this->assertEquals(true, $assign->testable_show_intro());
296         $tomorrow = time() + (24*60*60);
298         $assign = $this->create_instance(array('alwaysshowdescription'=>0,
299                                                'allowsubmissionsfromdate'=>$tomorrow));
300         $this->assertEquals(false, $assign->testable_show_intro());
301         $yesterday = time() - (24*60*60);
302         $assign = $this->create_instance(array('alwaysshowdescription'=>0,
303                                                'allowsubmissionsfromdate'=>$yesterday));
304         $this->assertEquals(true, $assign->testable_show_intro());
305     }
307     public function test_has_submissions_or_grades() {
308         $this->setUser($this->editingteachers[0]);
309         $assign = $this->create_instance(array('assignsubmission_onlinetext_enabled'=>1));
311         $instance = $assign->get_instance();
313         // Should start empty.
314         $this->assertEquals(false, $assign->has_submissions_or_grades());
316         // Simulate a submission.
317         $this->setUser($this->students[0]);
318         $submission = $assign->get_user_submission($this->students[0]->id, true);
319         $data = new stdClass();
320         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
321                                          'text'=>'Submission text',
322                                          'format'=>FORMAT_MOODLE);
323         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
324         $plugin->save($submission, $data);
326         // Now test again.
327         $this->assertEquals(true, $assign->has_submissions_or_grades());
328         // Set this back to default.
329         $this->students[0]->ignoresesskey = false;
330     }
332     public function test_delete_grades() {
333         $this->setUser($this->editingteachers[0]);
334         $assign = $this->create_instance();
336         // Simulate adding a grade.
337         $this->setUser($this->teachers[0]);
338         $data = new stdClass();
339         $data->grade = '50.0';
340         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
342         // Now see if the data is in the gradebook.
343         $gradinginfo = grade_get_grades($this->course->id,
344                                         'mod',
345                                         'assign',
346                                         $assign->get_instance()->id);
348         $this->assertNotEquals(0, count($gradinginfo->items));
350         $assign->testable_delete_grades();
351         $gradinginfo = grade_get_grades($this->course->id,
352                                         'mod',
353                                         'assign',
354                                         $assign->get_instance()->id);
356         $this->assertEquals(0, count($gradinginfo->items));
357     }
359     public function test_delete_instance() {
360         $this->setUser($this->editingteachers[0]);
361         $assign = $this->create_instance(array('assignsubmission_onlinetext_enabled'=>1));
363         // Simulate adding a grade.
364         $this->setUser($this->teachers[0]);
365         $data = new stdClass();
366         $data->grade = '50.0';
367         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
369         // Simulate a submission.
370         $this->setUser($this->students[0]);
371         $submission = $assign->get_user_submission($this->students[0]->id, true);
372         $data = new stdClass();
373         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
374                                          'text'=>'Submission text',
375                                          'format'=>FORMAT_MOODLE);
376         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
377         $plugin->save($submission, $data);
379         // Now try and delete.
380         $this->assertEquals(true, $assign->delete_instance());
381     }
383     public function test_reset_userdata() {
384         global $DB;
386         $now = time();
387         $this->setUser($this->editingteachers[0]);
388         $assign = $this->create_instance(array('assignsubmission_onlinetext_enabled'=>1,
389                                                'duedate'=>$now));
391         // Simulate adding a grade.
392         $this->setUser($this->teachers[0]);
393         $data = new stdClass();
394         $data->grade = '50.0';
395         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
397         // Simulate a submission.
398         $this->setUser($this->students[0]);
399         $submission = $assign->get_user_submission($this->students[0]->id, true);
400         $data = new stdClass();
401         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
402                                          'text'=>'Submission text',
403                                          'format'=>FORMAT_MOODLE);
404         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
405         $plugin->save($submission, $data);
407         $this->assertEquals(true, $assign->has_submissions_or_grades());
408         // Now try and reset.
409         $data = new stdClass();
410         $data->reset_assign_submissions = 1;
411         $data->reset_gradebook_grades = 1;
412         $data->courseid = $this->course->id;
413         $data->timeshift = 24*60*60;
414         $this->setUser($this->editingteachers[0]);
415         $assign->reset_userdata($data);
416         $this->assertEquals(false, $assign->has_submissions_or_grades());
418         // Reload the instance data.
419         $instance = $DB->get_record('assign', array('id'=>$assign->get_instance()->id));
420         $this->assertEquals($now + 24*60*60, $instance->duedate);
422         // Test reset using assign_reset_userdata().
423         $assignduedate = $instance->duedate; // Keep old updated value for comparison.
424         $data->timeshift = 2*24*60*60;
425         assign_reset_userdata($data);
426         $instance = $DB->get_record('assign', array('id' => $assign->get_instance()->id));
427         $this->assertEquals($assignduedate + 2*24*60*60, $instance->duedate);
429         // Create one more assignment and reset, make sure time shifted for previous assignment is not changed.
430         $assign2 = $this->create_instance(array('assignsubmission_onlinetext_enabled' => 1,
431                                                'duedate' => $now));
432         $assignduedate = $instance->duedate;
433         $data->timeshift = 3*24*60*60;
434         $assign2->reset_userdata($data);
435         $instance = $DB->get_record('assign', array('id' => $assign->get_instance()->id));
436         $this->assertEquals($assignduedate, $instance->duedate);
437         $instance2 = $DB->get_record('assign', array('id' => $assign2->get_instance()->id));
438         $this->assertEquals($now + 3*24*60*60, $instance2->duedate);
440         // Reset both assignments using assign_reset_userdata() and make sure both assignments have same date.
441         $assignduedate = $instance->duedate;
442         $assign2duedate = $instance2->duedate;
443         $data->timeshift = 4*24*60*60;
444         assign_reset_userdata($data);
445         $instance = $DB->get_record('assign', array('id' => $assign->get_instance()->id));
446         $this->assertEquals($assignduedate + 4*24*60*60, $instance->duedate);
447         $instance2 = $DB->get_record('assign', array('id' => $assign2->get_instance()->id));
448         $this->assertEquals($assign2duedate + 4*24*60*60, $instance2->duedate);
449     }
451     public function test_plugin_settings() {
452         global $DB;
454         $now = time();
455         $this->setUser($this->editingteachers[0]);
456         $assign = $this->create_instance(array('assignsubmission_file_enabled'=>1,
457                                                'assignsubmission_file_maxfiles'=>12,
458                                                'assignsubmission_file_maxsizebytes'=>10));
460         $plugin = $assign->get_submission_plugin_by_type('file');
461         $this->assertEquals('12', $plugin->get_config('maxfilesubmissions'));
462     }
464     public function test_update_calendar() {
465         global $DB;
467         $this->setUser($this->editingteachers[0]);
468         $userctx = context_user::instance($this->editingteachers[0]->id)->id;
470         // Hack to pretend that there was an editor involved. We need both $_POST and $_REQUEST, and a sesskey.
471         $draftid = file_get_unused_draft_itemid();
472         $_REQUEST['introeditor'] = $draftid;
473         $_POST['introeditor'] = $draftid;
474         $_POST['sesskey'] = sesskey();
476         // Write links to a draft area.
477         $fakearealink1 = file_rewrite_pluginfile_urls('<a href="@@PLUGINFILE@@/pic.gif">link</a>', 'draftfile.php', $userctx,
478             'user', 'draft', $draftid);
479         $fakearealink2 = file_rewrite_pluginfile_urls('<a href="@@PLUGINFILE@@/pic.gif">new</a>', 'draftfile.php', $userctx,
480             'user', 'draft', $draftid);
482         // Create a new assignment with links to a draft area.
483         $now = time();
484         $assign = $this->create_instance(array(
485             'duedate' => $now,
486             'intro' => $fakearealink1,
487             'introformat' => FORMAT_HTML
488         ));
490         // See if there is an event in the calendar.
491         $params = array('modulename'=>'assign', 'instance'=>$assign->get_instance()->id);
492         $event = $DB->get_record('event', $params);
493         $this->assertNotEmpty($event);
494         $this->assertSame('link', $event->description);     // The pluginfile links are removed.
496         // Make sure the same works when updating the assignment.
497         $instance = $assign->get_instance();
498         $instance->instance = $instance->id;
499         $instance->intro = $fakearealink2;
500         $instance->introformat = FORMAT_HTML;
501         $assign->update_instance($instance);
502         $params = array('modulename' => 'assign', 'instance' => $assign->get_instance()->id);
503         $event = $DB->get_record('event', $params);
504         $this->assertNotEmpty($event);
505         $this->assertSame('new', $event->description);     // The pluginfile links are removed.
507         // Create an assignment with a description that should be hidden.
508         $assign = $this->create_instance(array('duedate'=>$now + 160,
509                                                'alwaysshowdescription'=>false,
510                                                'allowsubmissionsfromdate'=>$now + 60,
511                                                'intro'=>'Some text'));
513         // Get the event from the calendar.
514         $params = array('modulename'=>'assign', 'instance'=>$assign->get_instance()->id);
515         $event = $DB->get_record('event', $params);
517         $this->assertEmpty($event->description);
519         // Change the allowsubmissionfromdate to the past - do this directly in the DB
520         // because if we call the assignment update method - it will update the calendar
521         // and we want to test that this works from cron.
522         $DB->set_field('assign', 'allowsubmissionsfromdate', $now - 60, array('id'=>$assign->get_instance()->id));
523         // Run cron to update the event in the calendar.
524         assign::cron();
525         $event = $DB->get_record('event', $params);
527         $this->assertContains('Some text', $event->description);
529     }
531     public function test_update_instance() {
532         global $DB;
534         $this->setUser($this->editingteachers[0]);
535         $assign = $this->create_instance(array('assignsubmission_onlinetext_enabled'=>1));
537         $now = time();
538         $instance = $assign->get_instance();
539         $instance->duedate = $now;
540         $instance->instance = $instance->id;
541         $instance->assignsubmission_onlinetext_enabled = 1;
543         $assign->update_instance($instance);
545         $instance = $DB->get_record('assign', array('id'=>$assign->get_instance()->id));
546         $this->assertEquals($now, $instance->duedate);
547     }
549     public function test_cannot_submit_empty() {
550         global $PAGE;
552         $this->setUser($this->editingteachers[0]);
553         $assign = $this->create_instance(array('submissiondrafts'=>1));
555         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
557         // Test you cannot see the submit button for an offline assignment regardless.
558         $this->setUser($this->students[0]);
559         $output = $assign->view_student_summary($this->students[0], true);
560         $this->assertNotContains(get_string('submitassignment', 'assign'), $output, 'Can submit empty offline assignment');
562         // Test you cannot see the submit button for an online text assignment with no submission.
563         $this->setUser($this->editingteachers[0]);
564         $instance = $assign->get_instance();
565         $instance->instance = $instance->id;
566         $instance->assignsubmission_onlinetext_enabled = 1;
568         $assign->update_instance($instance);
569         $this->setUser($this->students[0]);
570         $output = $assign->view_student_summary($this->students[0], true);
571         $this->assertNotContains(get_string('submitassignment', 'assign'), $output, 'Cannot submit empty onlinetext assignment');
573         // Simulate a submission.
574         $submission = $assign->get_user_submission($this->students[0]->id, true);
575         $data = new stdClass();
576         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
577                                          'text'=>'Submission text',
578                                          'format'=>FORMAT_MOODLE);
579         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
580         $plugin->save($submission, $data);
581         // Test you can see the submit button for an online text assignment with a submission.
582         $output = $assign->view_student_summary($this->students[0], true);
583         $this->assertContains(get_string('submitassignment', 'assign'), $output, 'Can submit non empty onlinetext assignment');
584     }
586     public function test_list_participants() {
587         global $CFG, $DB;
589         $this->create_extra_users();
590         $this->setUser($this->editingteachers[0]);
591         $assign = $this->create_instance(array('grade'=>100));
593         $this->assertEquals(self::DEFAULT_STUDENT_COUNT + self::EXTRA_STUDENT_COUNT, count($assign->list_participants(null, true)));
595         // Teacher with user preference set should see suspended users as well.
596         set_user_preference('grade_report_showonlyactiveenrol', false);
597         $assign = $this->create_instance(array('grade'=>100));
598         $this->assertEquals(self::DEFAULT_STUDENT_COUNT + self::EXTRA_STUDENT_COUNT + self::EXTRA_SUSPENDED_COUNT,
599                 count($assign->list_participants(null, true)));
601         // Non-editing teacher should not see suspended users, even if user preference is set.
602         $this->setUser($this->teachers[0]);
603         set_user_preference('grade_report_showonlyactiveenrol', false);
604         $assign = $this->create_instance(array('grade'=>100));
605         $this->assertEquals(self::DEFAULT_STUDENT_COUNT + self::EXTRA_STUDENT_COUNT, count($assign->list_participants(null, true)));
607         // Turn on availability and a group restriction, and check that it doesn't
608         // show users who aren't in the group.
609         $CFG->enableavailability = true;
610         $specialgroup = $this->getDataGenerator()->create_group(
611                 array('courseid' => $this->course->id));
612         $assign = $this->create_instance(array('grade' => 100,
613                 'availability' => json_encode(\core_availability\tree::get_root_json(
614                     array(\availability_group\condition::get_json($specialgroup->id))))));
615         groups_add_member($specialgroup, $this->students[0]);
616         groups_add_member($specialgroup, $this->students[1]);
617         $this->assertEquals(2, count($assign->list_participants(null, true)));
618     }
620     public function test_count_teams() {
621         $this->create_extra_users();
622         $this->setUser($this->editingteachers[0]);
623         $assign = $this->create_instance(array('teamsubmission'=>1));
625         $this->assertEquals(self::GROUP_COUNT + 1, $assign->count_teams());
626     }
628     public function test_submit_to_default_group() {
629         global $DB;
631         $this->preventResetByRollback();
632         $sink = $this->redirectMessages();
634         $this->setUser($this->editingteachers[0]);
635         $params = array('teamsubmission' => 1,
636                         'assignsubmission_onlinetext_enabled' => 1,
637                         'submissiondrafts'=>0);
638         $assign = $this->create_instance($params);
640         $newstudent = $this->getDataGenerator()->create_user();
641         $studentrole = $DB->get_record('role', array('shortname'=>'student'));
642         $this->getDataGenerator()->enrol_user($newstudent->id,
643                                               $this->course->id,
644                                               $studentrole->id);
645         $this->setUser($newstudent);
646         $data = new stdClass();
647         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
648                                          'text'=>'Submission text',
649                                          'format'=>FORMAT_MOODLE);
650         $notices = array();
652         $group = $assign->get_submission_group($newstudent->id);
653         $this->assertFalse($group, 'New student is in default group');
654         $assign->save_submission($data, $notices);
655         $this->assertEmpty($notices, 'No errors on save submission');
657         $sink->close();
658     }
660     public function test_count_submissions() {
661         $this->create_extra_users();
662         $this->setUser($this->editingteachers[0]);
663         $assign = $this->create_instance(array('assignsubmission_onlinetext_enabled'=>1));
665         // Simulate a submission.
666         $this->setUser($this->extrastudents[0]);
667         $submission = $assign->get_user_submission($this->extrastudents[0]->id, true);
668         $submission->status = ASSIGN_SUBMISSION_STATUS_DRAFT;
669         $assign->testable_update_submission($submission, $this->extrastudents[0]->id, true, false);
670         // Leave this one as DRAFT.
671         $data = new stdClass();
672         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
673                                          'text'=>'Submission text',
674                                          'format'=>FORMAT_MOODLE);
675         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
676         $plugin->save($submission, $data);
678         // Simulate adding a grade.
679         $this->setUser($this->teachers[0]);
680         $data = new stdClass();
681         $data->grade = '50.0';
682         $assign->testable_apply_grade_to_user($data, $this->extrastudents[0]->id, 0);
684         // Simulate a submission.
685         $this->setUser($this->extrastudents[1]);
686         $submission = $assign->get_user_submission($this->extrastudents[1]->id, true);
687         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
688         $assign->testable_update_submission($submission, $this->extrastudents[1]->id, true, false);
689         $data = new stdClass();
690         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
691                                          'text'=>'Submission text',
692                                          'format'=>FORMAT_MOODLE);
693         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
694         $plugin->save($submission, $data);
696         // Simulate a submission.
697         $this->setUser($this->extrastudents[2]);
698         $submission = $assign->get_user_submission($this->extrastudents[2]->id, true);
699         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
700         $assign->testable_update_submission($submission, $this->extrastudents[2]->id, true, false);
701         $data = new stdClass();
702         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
703                                          'text'=>'Submission text',
704                                          'format'=>FORMAT_MOODLE);
705         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
706         $plugin->save($submission, $data);
708         // Simulate a submission.
709         $this->setUser($this->extrastudents[3]);
710         $submission = $assign->get_user_submission($this->extrastudents[3]->id, true);
711         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
712         $assign->testable_update_submission($submission, $this->extrastudents[3]->id, true, false);
713         $data = new stdClass();
714         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
715                                          'text'=>'Submission text',
716                                          'format'=>FORMAT_MOODLE);
717         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
718         $plugin->save($submission, $data);
720         // Simulate a submission for suspended user, this will never be counted.
721         $this->setUser($this->extrastudents[3]);
722         $submission = $assign->get_user_submission($this->extrasuspendedstudents[0]->id, true);
723         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
724         $assign->testable_update_submission($submission, $this->extrasuspendedstudents[0]->id, true, false);
725         $data = new stdClass();
726         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
727                                          'text'=>'Submission text',
728                                          'format'=>FORMAT_MOODLE);
729         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
730         $plugin->save($submission, $data);
732         // Simulate adding a grade.
733         $this->setUser($this->teachers[0]);
734         $data = new stdClass();
735         $data->grade = '50.0';
736         $assign->testable_apply_grade_to_user($data, $this->extrastudents[3]->id, 0);
737         $assign->testable_apply_grade_to_user($data, $this->extrasuspendedstudents[0]->id, 0);
739         $this->assertEquals(2, $assign->count_grades());
740         $this->assertEquals(4, $assign->count_submissions());
741         $this->assertEquals(2, $assign->count_submissions_need_grading());
742         $this->assertEquals(3, $assign->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_SUBMITTED));
743         $this->assertEquals(1, $assign->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_DRAFT));
744     }
746     public function test_get_grading_userid_list() {
747         $this->create_extra_users();
748         $this->setUser($this->editingteachers[0]);
749         $assign = $this->create_instance();
751         $users = $assign->testable_get_grading_userid_list();
752         $this->assertEquals(self::DEFAULT_STUDENT_COUNT + self::EXTRA_STUDENT_COUNT, count($users));
754         $this->setUser($this->editingteachers[0]);
755         set_user_preference('grade_report_showonlyactiveenrol', false);
756         $assign = $this->create_instance();
758         $users = $assign->testable_get_grading_userid_list();
759         $this->assertEquals(self::DEFAULT_STUDENT_COUNT + self::EXTRA_STUDENT_COUNT + self::EXTRA_SUSPENDED_COUNT, count($users));
760     }
762     public function test_cron() {
763         // First run cron so there are no messages waiting to be sent (from other tests).
764         cron_setup_user();
765         assign::cron();
767         // Now create an assignment and add some feedback.
768         $this->setUser($this->editingteachers[0]);
769         $assign = $this->create_instance(array('sendstudentnotifications'=>1));
771         // Simulate adding a grade.
772         $this->setUser($this->teachers[0]);
773         $data = new stdClass();
774         $data->grade = '50.0';
775         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
776         $assign->testable_apply_grade_to_user($data, $this->students[1]->id, 0);
778         $data->sendstudentnotifications = false;
779         $assign->testable_apply_grade_to_user($data, $this->students[2]->id, 0);
781         // Now run cron and see that one message was sent.
782         $this->preventResetByRollback();
783         $sink = $this->redirectMessages();
784         cron_setup_user();
785         $this->expectOutputRegex('/Done processing 2 assignment submissions/');
786         assign::cron();
788         $messages = $sink->get_messages();
789         // The sent count should be 2, because the 3rd one was marked as do not send notifications.
790         $this->assertEquals(2, count($messages));
791         $this->assertEquals(1, $messages[0]->notification);
792         $this->assertEquals($assign->get_instance()->name, $messages[0]->contexturlname);
793     }
795     public function test_is_graded() {
796         $this->setUser($this->editingteachers[0]);
797         $assign = $this->create_instance();
799         // Simulate adding a grade.
800         $this->setUser($this->teachers[0]);
801         $data = new stdClass();
802         $data->grade = '50.0';
803         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
805         $this->assertEquals(true, $assign->testable_is_graded($this->students[0]->id));
806         $this->assertEquals(false, $assign->testable_is_graded($this->students[1]->id));
807     }
809     public function test_can_grade() {
810         global $DB;
812         $this->setUser($this->editingteachers[0]);
813         $assign = $this->create_instance();
815         $this->setUser($this->students[0]);
816         $this->assertEquals(false, $assign->can_grade());
817         $this->setUser($this->editingteachers[0]);
818         $this->assertEquals(true, $assign->can_grade());
819         $this->setUser($this->teachers[0]);
820         $this->assertEquals(true, $assign->can_grade());
822         // Test the viewgrades capability - without mod/assign:grade.
823         $this->setUser($this->students[0]);
824         $studentrole = $DB->get_record('role', array('shortname' => 'student'));
825         assign_capability('mod/assign:viewgrades', CAP_ALLOW, $studentrole->id, $assign->get_context()->id);
826         $this->assertEquals(false, $assign->can_grade());
827     }
829     public function test_can_view_submission() {
830         global $DB;
832         $this->create_extra_users();
833         $this->setUser($this->editingteachers[0]);
834         $assign = $this->create_instance();
836         $this->setUser($this->students[0]);
837         $this->assertEquals(true, $assign->can_view_submission($this->students[0]->id));
838         $this->assertEquals(false, $assign->can_view_submission($this->students[1]->id));
839         $this->assertEquals(false, $assign->can_view_submission($this->teachers[0]->id));
840         $this->setUser($this->teachers[0]);
841         $this->assertEquals(true, $assign->can_view_submission($this->students[0]->id));
842         $this->assertEquals(true, $assign->can_view_submission($this->students[1]->id));
843         $this->assertEquals(true, $assign->can_view_submission($this->teachers[0]->id));
844         $this->assertEquals(false, $assign->can_view_submission($this->extrasuspendedstudents[0]->id));
845         $this->setUser($this->editingteachers[0]);
846         $this->assertEquals(true, $assign->can_view_submission($this->students[0]->id));
847         $this->assertEquals(true, $assign->can_view_submission($this->students[1]->id));
848         $this->assertEquals(true, $assign->can_view_submission($this->teachers[0]->id));
849         $this->assertEquals(true, $assign->can_view_submission($this->extrasuspendedstudents[0]->id));
851         // Test the viewgrades capability - without mod/assign:grade.
852         $this->setUser($this->students[0]);
853         $studentrole = $DB->get_record('role', array('shortname' => 'student'));
854         assign_capability('mod/assign:viewgrades', CAP_ALLOW, $studentrole->id, $assign->get_context()->id);
855         $this->assertEquals(true, $assign->can_view_submission($this->students[0]->id));
856         $this->assertEquals(true, $assign->can_view_submission($this->students[1]->id));
857         $this->assertEquals(true, $assign->can_view_submission($this->teachers[0]->id));
858         $this->assertEquals(false, $assign->can_view_submission($this->extrasuspendedstudents[0]->id));
859     }
862     public function test_update_submission() {
863         $this->create_extra_users();
864         $this->setUser($this->editingteachers[0]);
865         $assign = $this->create_instance();
867         $this->setUser($this->extrastudents[0]);
868         $now = time();
869         $submission = $assign->get_user_submission($this->extrastudents[0]->id, true);
870         $assign->testable_update_submission($submission, $this->extrastudents[0]->id, true, false);
872         $this->setUser($this->teachers[0]);
873         // Verify the gradebook update.
874         $gradinginfo = grade_get_grades($this->course->id,
875                                         'mod',
876                                         'assign',
877                                         $assign->get_instance()->id,
878                                         $this->extrastudents[0]->id);
880         $this->assertEquals($this->extrastudents[0]->id,
881                             $gradinginfo->items[0]->grades[$this->extrastudents[0]->id]->usermodified);
883         // Now verify group assignments.
884         $this->setUser($this->editingteachers[0]);
885         $assign = $this->create_instance(array('teamsubmission'=>1));
887         $this->setUser($this->extrastudents[0]);
888         $now = time();
889         $submission = $assign->get_group_submission($this->extrastudents[0]->id, 0, true);
890         $assign->testable_update_submission($submission, $this->extrastudents[0]->id, true, true);
892         // Check that at least 2 active members and 1 suspended member of the submission group had their submission updated.
894         $this->setUser($this->editingteachers[0]);
895         $gradinginfo = grade_get_grades($this->course->id,
896                                         'mod',
897                                         'assign',
898                                         $assign->get_instance()->id,
899                                         $this->extrastudents[0]->id);
901         $this->assertEquals($this->extrastudents[0]->id,
902                             $gradinginfo->items[0]->grades[$this->extrastudents[0]->id]->usermodified);
904         $gradinginfo = grade_get_grades($this->course->id,
905                                         'mod',
906                                         'assign',
907                                         $assign->get_instance()->id,
908                                         $this->extrastudents[self::GROUP_COUNT]->id);
910         $this->assertEquals($this->extrastudents[self::GROUP_COUNT]->id,
911                             $gradinginfo->items[0]->grades[$this->extrastudents[self::GROUP_COUNT]->id]->usermodified);
913         $gradinginfo = grade_get_grades($this->course->id,
914                                         'mod',
915                                         'assign',
916                                         $assign->get_instance()->id,
917                                         $this->extrasuspendedstudents[0]->id);
918         $this->assertEquals($this->extrasuspendedstudents[0]->id,
919                             $gradinginfo->items[0]->grades[$this->extrasuspendedstudents[0]->id]->usermodified);
921         // Check the same with non-editing teacher and make sure submission is not updated for suspended user.
922         $this->setUser($this->editingteachers[0]);
923         $assign = $this->create_instance(array('teamsubmission'=>1));
925         $this->setUser($this->extrastudents[1]);
926         $now = time();
927         $submission = $assign->get_group_submission($this->extrastudents[1]->id, 0, true);
928         $assign->testable_update_submission($submission, $this->extrastudents[1]->id, true, true);
930         $this->setUser($this->teachers[0]);
931         $gradinginfo = grade_get_grades($this->course->id,
932                                         'mod',
933                                         'assign',
934                                         $assign->get_instance()->id,
935                                         $this->extrastudents[1]->id);
937         $this->assertEquals($this->extrastudents[1]->id,
938                             $gradinginfo->items[0]->grades[$this->extrastudents[1]->id]->usermodified);
940         $gradinginfo = grade_get_grades($this->course->id,
941                                         'mod',
942                                         'assign',
943                                         $assign->get_instance()->id,
944                                         $this->extrastudents[self::GROUP_COUNT+1]->id);
946         $this->assertEquals($this->extrastudents[self::GROUP_COUNT+1]->id,
947                             $gradinginfo->items[0]->grades[$this->extrastudents[self::GROUP_COUNT+1]->id]->usermodified);
949         $gradinginfo = grade_get_grades($this->course->id,
950                                         'mod',
951                                         'assign',
952                                         $assign->get_instance()->id,
953                                         $this->extrasuspendedstudents[1]->id);
954         $this->assertEquals($this->extrasuspendedstudents[1]->id,
955                             $gradinginfo->items[0]->grades[$this->extrasuspendedstudents[1]->id]->usermodified);
957         // Now verify blind marking.
958         $this->setUser($this->editingteachers[0]);
959         $assign = $this->create_instance(array('blindmarking'=>1));
961         $this->setUser($this->extrastudents[0]);
962         $now = time();
963         $submission = $assign->get_user_submission($this->extrastudents[0]->id, true);
964         $assign->testable_update_submission($submission, $this->extrastudents[0]->id, true, false);
966         $this->setUser($this->editingteachers[0]);
967         $gradinginfo = grade_get_grades($this->course->id,
968                                         'mod',
969                                         'assign',
970                                         $assign->get_instance()->id,
971                                         $this->extrastudents[0]->id);
973         $this->assertEquals(null, $gradinginfo->items[0]->grades[$this->extrastudents[0]->id]->datesubmitted);
974     }
976     public function test_group_submissions_submit_for_marking_requireallteammemberssubmit() {
977         global $PAGE;
979         $this->create_extra_users();
980         // Now verify group assignments.
981         $this->setUser($this->editingteachers[0]);
982         $assign = $this->create_instance(array('teamsubmission'=>1,
983                                                'assignsubmission_onlinetext_enabled'=>1,
984                                                'submissiondrafts'=>1,
985                                                'requireallteammemberssubmit'=>1));
986         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
988         // Add a submission.
989         $this->setUser($this->extrastudents[0]);
990         $data = new stdClass();
991         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
992                                          'text'=>'Submission text',
993                                          'format'=>FORMAT_MOODLE);
995         $notices = array();
996         $assign->save_submission($data, $notices);
998         // Check we can see the submit button.
999         $output = $assign->view_student_summary($this->extrastudents[0], true);
1000         $this->assertContains(get_string('submitassignment', 'assign'), $output);
1002         $submission = $assign->get_group_submission($this->extrastudents[0]->id, 0, true);
1003         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
1004         $assign->testable_update_submission($submission, $this->extrastudents[0]->id, true, true);
1006         // Check that the student does not see "Submit" button.
1007         $output = $assign->view_student_summary($this->extrastudents[0], true);
1008         $this->assertNotContains(get_string('submitassignment', 'assign'), $output);
1010         // Change to another user in the same group.
1011         $this->setUser($this->extrastudents[self::GROUP_COUNT]);
1012         $output = $assign->view_student_summary($this->extrastudents[self::GROUP_COUNT], true);
1013         $this->assertContains(get_string('submitassignment', 'assign'), $output);
1015         $submission = $assign->get_group_submission($this->extrastudents[self::GROUP_COUNT]->id, 0, true);
1016         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
1017         $assign->testable_update_submission($submission, $this->extrastudents[self::GROUP_COUNT]->id, true, true);
1018         $output = $assign->view_student_summary($this->extrastudents[self::GROUP_COUNT], true);
1019         $this->assertNotContains(get_string('submitassignment', 'assign'), $output);
1020     }
1022     public function test_group_submissions_submit_for_marking() {
1023         global $PAGE;
1025         $this->create_extra_users();
1026         // Now verify group assignments.
1027         $this->setUser($this->editingteachers[0]);
1028         $assign = $this->create_instance(array('teamsubmission'=>1,
1029                                                'assignsubmission_onlinetext_enabled'=>1,
1030                                                'submissiondrafts'=>1,
1031                                                'requireallteammemberssubmit'=>0,
1032                                                'duedate' => time() - 2*24*60*60));
1033         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
1035         $this->setUser($this->extrastudents[0]);
1036         // Add a submission.
1037         $data = new stdClass();
1038         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
1039                                          'text'=>'Submission text',
1040                                          'format'=>FORMAT_MOODLE);
1042         $notices = array();
1043         $assign->save_submission($data, $notices);
1045         // Check we can see the submit button.
1046         $output = $assign->view_student_summary($this->extrastudents[0], true);
1047         $this->assertContains(get_string('submitassignment', 'assign'), $output);
1048         $this->assertContains(get_string('timeremaining', 'assign'), $output);
1049         $this->assertContains(get_string('overdue', 'assign', format_time(2*24*60*60)), $output);
1051         $submission = $assign->get_group_submission($this->extrastudents[0]->id, 0, true);
1052         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
1053         $assign->testable_update_submission($submission, $this->extrastudents[0]->id, true, true);
1055         // Check that the student does not see "Submit" button.
1056         $output = $assign->view_student_summary($this->extrastudents[0], true);
1057         $this->assertNotContains(get_string('submitassignment', 'assign'), $output);
1059         // Change to another user in the same group.
1060         $this->setUser($this->extrastudents[self::GROUP_COUNT]);
1061         $output = $assign->view_student_summary($this->extrastudents[self::GROUP_COUNT], true);
1062         $this->assertNotContains(get_string('submitassignment', 'assign'), $output);
1064         // Check that time remaining is not overdue.
1065         $this->assertContains(get_string('timeremaining', 'assign'), $output);
1066         $this->assertContains(get_string('submittedlate', 'assign', format_time(2*24*60*60)), $output);
1068         $submission = $assign->get_group_submission($this->extrastudents[self::GROUP_COUNT]->id, 0, true);
1069         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
1070         $assign->testable_update_submission($submission, $this->extrastudents[self::GROUP_COUNT]->id, true, true);
1071         $output = $assign->view_student_summary($this->extrastudents[self::GROUP_COUNT], true);
1072         $this->assertNotContains(get_string('submitassignment', 'assign'), $output);
1073     }
1075     public function test_submissions_open() {
1076         $this->setUser($this->editingteachers[0]);
1078         $now = time();
1079         $tomorrow = $now + 24*60*60;
1080         $oneweek = $now + 7*24*60*60;
1081         $yesterday = $now - 24*60*60;
1083         $assign = $this->create_instance();
1084         $this->assertEquals(true, $assign->testable_submissions_open($this->students[0]->id));
1086         $assign = $this->create_instance(array('duedate'=>$tomorrow));
1087         $this->assertEquals(true, $assign->testable_submissions_open($this->students[0]->id));
1089         $assign = $this->create_instance(array('duedate'=>$yesterday));
1090         $this->assertEquals(true, $assign->testable_submissions_open($this->students[0]->id));
1092         $assign = $this->create_instance(array('duedate'=>$yesterday, 'cutoffdate'=>$tomorrow));
1093         $this->assertEquals(true, $assign->testable_submissions_open($this->students[0]->id));
1095         $assign = $this->create_instance(array('duedate'=>$yesterday, 'cutoffdate'=>$yesterday));
1096         $this->assertEquals(false, $assign->testable_submissions_open($this->students[0]->id));
1098         $assign->testable_save_user_extension($this->students[0]->id, $tomorrow);
1099         $this->assertEquals(true, $assign->testable_submissions_open($this->students[0]->id));
1101         $assign = $this->create_instance(array('submissiondrafts'=>1));
1102         $this->assertEquals(true, $assign->testable_submissions_open($this->students[0]->id));
1104         $this->setUser($this->students[0]);
1105         $now = time();
1106         $submission = $assign->get_user_submission($this->students[0]->id, true);
1107         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
1108         $assign->testable_update_submission($submission, $this->students[0]->id, true, false);
1109         $this->setUser($this->editingteachers[0]);
1110         $this->assertEquals(false, $assign->testable_submissions_open($this->students[0]->id));
1111     }
1113     public function test_get_graders() {
1114         $this->create_extra_users();
1115         $this->setUser($this->editingteachers[0]);
1117         // Create an assignment with no groups.
1118         $assign = $this->create_instance();
1119         $this->assertCount(self::DEFAULT_TEACHER_COUNT +
1120                            self::DEFAULT_EDITING_TEACHER_COUNT +
1121                            self::EXTRA_TEACHER_COUNT +
1122                            self::EXTRA_EDITING_TEACHER_COUNT,
1123                            $assign->testable_get_graders($this->students[0]->id));
1125         // Force create an assignment with SEPARATEGROUPS.
1126         $data = new stdClass();
1127         $data->courseid = $this->course->id;
1128         $data->name = 'Grouping';
1129         $groupingid = groups_create_grouping($data);
1130         groups_assign_grouping($groupingid, $this->groups[0]->id);
1131         $assign = $this->create_instance(array('groupingid' => $groupingid, 'groupmode' => SEPARATEGROUPS));
1133         $this->setUser($this->students[1]);
1134         $this->assertCount(4, $assign->testable_get_graders($this->students[0]->id));
1135         // Note the second student is in a group that is not in the grouping.
1136         // This means that we get all graders that are not in a group in the grouping.
1137         $this->assertCount(10, $assign->testable_get_graders($this->students[1]->id));
1138     }
1140     public function test_get_notified_users() {
1141         global $CFG, $DB;
1143         $capability = 'mod/assign:receivegradernotifications';
1144         $coursecontext = context_course::instance($this->course->id);
1145         $role = $DB->get_record('role', array('shortname' => 'teacher'));
1147         $this->create_extra_users();
1148         $this->setUser($this->editingteachers[0]);
1150         // Create an assignment with no groups.
1151         $assign = $this->create_instance();
1153         $this->assertCount(self::DEFAULT_TEACHER_COUNT +
1154                            self::DEFAULT_EDITING_TEACHER_COUNT +
1155                            self::EXTRA_TEACHER_COUNT +
1156                            self::EXTRA_EDITING_TEACHER_COUNT,
1157                            $assign->testable_get_notifiable_users($this->students[0]->id));
1159         // Change nonediting teachers role to not receive grader notifications.
1160         assign_capability($capability, CAP_PROHIBIT, $role->id, $coursecontext);
1162         $this->assertCount(self::DEFAULT_EDITING_TEACHER_COUNT +
1163                            self::EXTRA_EDITING_TEACHER_COUNT,
1164                            $assign->testable_get_notifiable_users($this->students[0]->id));
1166         // Reset nonediting teachers role to default.
1167         unassign_capability($capability, $role->id, $coursecontext);
1169         // Force create an assignment with SEPARATEGROUPS.
1170         $data = new stdClass();
1171         $data->courseid = $this->course->id;
1172         $data->name = 'Grouping';
1173         $groupingid = groups_create_grouping($data);
1174         groups_assign_grouping($groupingid, $this->groups[0]->id);
1175         $assign = $this->create_instance(array('groupingid' => $groupingid, 'groupmode' => SEPARATEGROUPS));
1177         $this->setUser($this->students[1]);
1178         $this->assertCount(4, $assign->testable_get_notifiable_users($this->students[0]->id));
1179         // Note the second student is in a group that is not in the grouping.
1180         // This means that we get all graders that are not in a group in the grouping.
1181         $this->assertCount(10, $assign->testable_get_notifiable_users($this->students[1]->id));
1183         // Change nonediting teachers role to not receive grader notifications.
1184         assign_capability($capability, CAP_PROHIBIT, $role->id, $coursecontext);
1186         $this->assertCount(2, $assign->testable_get_notifiable_users($this->students[0]->id));
1187         // Note the second student is in a group that is not in the grouping.
1188         // This means that we get all graders that are not in a group in the grouping.
1189         $this->assertCount(5, $assign->testable_get_notifiable_users($this->students[1]->id));
1190     }
1192     public function test_group_members_only() {
1193         global $CFG;
1195         $this->setAdminUser();
1196         $this->create_extra_users();
1197         $CFG->enableavailability = true;
1198         $grouping = $this->getDataGenerator()->create_grouping(array('courseid' => $this->course->id));
1199         groups_assign_grouping($grouping->id, $this->groups[0]->id);
1201         // Force create an assignment with SEPARATEGROUPS.
1202         $instance = $this->getDataGenerator()->create_module('assign', array('course'=>$this->course->id),
1203                 array('availability' => json_encode(\core_availability\tree::get_root_json(array(
1204                     \availability_grouping\condition::get_json()))),
1205                 'groupingid' => $grouping->id));
1207         $cm = get_coursemodule_from_instance('assign', $instance->id);
1208         $context = context_module::instance($cm->id);
1209         $assign = new testable_assign($context, $cm, $this->course);
1211         $this->setUser($this->teachers[0]);
1212         get_fast_modinfo($this->course, 0, true);
1213         $this->assertCount(5, $assign->list_participants(0, true));
1215     }
1217     public function test_get_uniqueid_for_user() {
1218         $this->setUser($this->editingteachers[0]);
1219         $assign = $this->create_instance();
1221         foreach ($this->students as $student) {
1222             $uniqueid = $assign->get_uniqueid_for_user($student->id);
1223             $this->assertEquals($student->id, $assign->get_user_id_for_uniqueid($uniqueid));
1224         }
1225     }
1227     public function test_show_student_summary() {
1228         global $CFG, $PAGE;
1230         $this->setUser($this->editingteachers[0]);
1231         $assign = $this->create_instance();
1232         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
1234         // No feedback should be available because this student has not been graded.
1235         $this->setUser($this->students[0]);
1236         $output = $assign->view_student_summary($this->students[0], true);
1237         $this->assertEquals(false, strpos($output, 'Feedback'), 'Do not show feedback if there is no grade');
1238         // Simulate adding a grade.
1239         $this->setUser($this->teachers[0]);
1240         $data = new stdClass();
1241         $data->grade = '50.0';
1242         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
1244         // Now we should see the feedback.
1245         $this->setUser($this->students[0]);
1246         $output = $assign->view_student_summary($this->students[0], true);
1247         $this->assertNotEquals(false, strpos($output, 'Feedback'), 'Show feedback if there is a grade');
1249         // Now hide the grade in gradebook.
1250         $this->setUser($this->teachers[0]);
1251         require_once($CFG->libdir.'/gradelib.php');
1252         $gradeitem = new grade_item(array(
1253             'itemtype'      => 'mod',
1254             'itemmodule'    => 'assign',
1255             'iteminstance'  => $assign->get_instance()->id,
1256             'courseid'      => $this->course->id));
1258         $gradeitem->set_hidden(1, false);
1260         // No feedback should be available because the grade is hidden.
1261         $this->setUser($this->students[0]);
1262         $output = $assign->view_student_summary($this->students[0], true);
1263         $this->assertEquals(false, strpos($output, 'Feedback'), 'Do not show feedback if the grade is hidden in the gradebook');
1265         // Do the same but add feedback.
1266         $assign = $this->create_instance(array('assignfeedback_comments_enabled' => 1));
1268         $this->setUser($this->teachers[0]);
1269         $grade = $assign->get_user_grade($this->students[0]->id, true);
1270         $data = new stdClass();
1271         $data->assignfeedbackcomments_editor = array('text'=>'Tomato sauce',
1272                                          'format'=>FORMAT_MOODLE);
1273         $plugin = $assign->get_feedback_plugin_by_type('comments');
1274         $plugin->save($grade, $data);
1276         // Should have feedback but no grade.
1277         $this->setUser($this->students[0]);
1278         $output = $assign->view_student_summary($this->students[0], true);
1279         $this->assertNotEquals(false, strpos($output, 'Feedback'), 'Show feedback even if there is no grade');
1280         $this->assertEquals(false, strpos($output, 'Grade'), 'Do not show grade when there is no grade.');
1281         $this->assertEquals(false, strpos($output, 'Graded on'), 'Do not show graded date when there is no grade.');
1283         // Now hide the grade in gradebook.
1284         $this->setUser($this->teachers[0]);
1285         $gradeitem = new grade_item(array(
1286             'itemtype'      => 'mod',
1287             'itemmodule'    => 'assign',
1288             'iteminstance'  => $assign->get_instance()->id,
1289             'courseid'      => $this->course->id));
1291         $gradeitem->set_hidden(1, false);
1293         // No feedback should be available because the grade is hidden.
1294         $this->setUser($this->students[0]);
1295         $output = $assign->view_student_summary($this->students[0], true);
1296         $this->assertEquals(false, strpos($output, 'Feedback'), 'Do not show feedback if the grade is hidden in the gradebook');
1297     }
1299     public function test_attempt_reopen_method_manual() {
1300         global $PAGE;
1302         $this->setUser($this->editingteachers[0]);
1303         $assign = $this->create_instance(array('attemptreopenmethod'=>ASSIGN_ATTEMPT_REOPEN_METHOD_MANUAL,
1304                                                'maxattempts'=>3,
1305                                                'submissiondrafts'=>1,
1306                                                'assignsubmission_onlinetext_enabled'=>1));
1307         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
1309         // Student should be able to see an add submission button.
1310         $this->setUser($this->students[0]);
1311         $output = $assign->view_student_summary($this->students[0], true);
1312         $this->assertNotEquals(false, strpos($output, get_string('addsubmission', 'assign')));
1314         // Add a submission.
1315         $now = time();
1316         $submission = $assign->get_user_submission($this->students[0]->id, true);
1317         $data = new stdClass();
1318         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
1319                                          'text'=>'Submission text',
1320                                          'format'=>FORMAT_MOODLE);
1321         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
1322         $plugin->save($submission, $data);
1324         // And now submit it for marking.
1325         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
1326         $assign->testable_update_submission($submission, $this->students[0]->id, true, false);
1328         // Verify the student cannot make changes to the submission.
1329         $output = $assign->view_student_summary($this->students[0], true);
1330         $this->assertEquals(false, strpos($output, get_string('addsubmission', 'assign')));
1332         // Mark the submission.
1333         $this->setUser($this->teachers[0]);
1334         $data = new stdClass();
1335         $data->grade = '50.0';
1336         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
1338         // Check the student can see the grade.
1339         $this->setUser($this->students[0]);
1340         $output = $assign->view_student_summary($this->students[0], true);
1341         $this->assertNotEquals(false, strpos($output, '50.0'));
1343         // Allow the student another attempt.
1344         $this->teachers[0]->ignoresesskey = true;
1345         $this->setUser($this->teachers[0]);
1346         $result = $assign->testable_process_add_attempt($this->students[0]->id);
1347         $this->assertEquals(true, $result);
1349         // Check that the previous attempt is now in the submission history table.
1350         $this->setUser($this->students[0]);
1351         $output = $assign->view_student_summary($this->students[0], true);
1352         // Need a better check.
1353         $this->assertNotEquals(false, strpos($output, 'Submission text'), 'Contains: Submission text');
1355         // Check that the student now has a button for Add a new attempt".
1356         $this->assertNotEquals(false, strpos($output, get_string('addnewattempt', 'assign')));
1357         // Check that the student now does not have a button for Submit.
1358         $this->assertEquals(false, strpos($output, get_string('submitassignment', 'assign')));
1360         // Check that the student now has a submission history.
1361         $this->assertNotEquals(false, strpos($output, get_string('attempthistory', 'assign')));
1363         $this->setUser($this->teachers[0]);
1364         // Check that the grading table loads correctly and contains this user.
1365         // This is also testing that we do not get duplicate rows in the grading table.
1366         $gradingtable = new assign_grading_table($assign, 100, '', 0, true);
1367         $output = $assign->get_renderer()->render($gradingtable);
1368         $this->assertEquals(true, strpos($output, $this->students[0]->lastname));
1370         // Should be 1 not 2.
1371         $this->assertEquals(1, $assign->count_submissions());
1372         $this->assertEquals(1, $assign->count_submissions_with_status('reopened'));
1373         $this->assertEquals(0, $assign->count_submissions_need_grading());
1374         $this->assertEquals(1, $assign->count_grades());
1376         // Change max attempts to unlimited.
1377         $formdata = clone($assign->get_instance());
1378         $formdata->maxattempts = ASSIGN_UNLIMITED_ATTEMPTS;
1379         $formdata->instance = $formdata->id;
1380         $assign->update_instance($formdata);
1382         // Mark the submission again.
1383         $data = new stdClass();
1384         $data->grade = '60.0';
1385         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 1);
1387         // Check the grade exists.
1388         $grades = $assign->get_user_grades_for_gradebook($this->students[0]->id);
1389         $this->assertEquals(60, (int)$grades[$this->students[0]->id]->rawgrade);
1391         // Check we can reopen still.
1392         $result = $assign->testable_process_add_attempt($this->students[0]->id);
1393         $this->assertEquals(true, $result);
1395         // Should no longer have a grade because there is no grade for the latest attempt.
1396         $grades = $assign->get_user_grades_for_gradebook($this->students[0]->id);
1397         $this->assertEmpty($grades);
1399     }
1401     public function test_markingworkflow() {
1402         global $PAGE;
1404         $this->setUser($this->editingteachers[0]);
1405         $assign = $this->create_instance(array('markingworkflow'=>1));
1406         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
1408         // Mark the submission and set to notmarked.
1409         $this->setUser($this->teachers[0]);
1410         $data = new stdClass();
1411         $data->grade = '50.0';
1412         $data->workflowstate = ASSIGN_MARKING_WORKFLOW_STATE_NOTMARKED;
1413         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
1415         // Check the student can't see the grade.
1416         $this->setUser($this->students[0]);
1417         $output = $assign->view_student_summary($this->students[0], true);
1418         $this->assertEquals(false, strpos($output, '50.0'));
1420         // Mark the submission and set to inmarking.
1421         $this->setUser($this->teachers[0]);
1422         $data = new stdClass();
1423         $data->grade = '50.0';
1424         $data->workflowstate = ASSIGN_MARKING_WORKFLOW_STATE_INMARKING;
1425         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
1427         // Check the student can't see the grade.
1428         $this->setUser($this->students[0]);
1429         $output = $assign->view_student_summary($this->students[0], true);
1430         $this->assertEquals(false, strpos($output, '50.0'));
1432         // Mark the submission and set to readyforreview.
1433         $this->setUser($this->teachers[0]);
1434         $data = new stdClass();
1435         $data->grade = '50.0';
1436         $data->workflowstate = ASSIGN_MARKING_WORKFLOW_STATE_READYFORREVIEW;
1437         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
1439         // Check the student can't see the grade.
1440         $this->setUser($this->students[0]);
1441         $output = $assign->view_student_summary($this->students[0], true);
1442         $this->assertEquals(false, strpos($output, '50.0'));
1444         // Mark the submission and set to inreview.
1445         $this->setUser($this->teachers[0]);
1446         $data = new stdClass();
1447         $data->grade = '50.0';
1448         $data->workflowstate = ASSIGN_MARKING_WORKFLOW_STATE_INREVIEW;
1449         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
1451         // Check the student can't see the grade.
1452         $this->setUser($this->students[0]);
1453         $output = $assign->view_student_summary($this->students[0], true);
1454         $this->assertEquals(false, strpos($output, '50.0'));
1456         // Mark the submission and set to readyforrelease.
1457         $this->setUser($this->teachers[0]);
1458         $data = new stdClass();
1459         $data->grade = '50.0';
1460         $data->workflowstate = ASSIGN_MARKING_WORKFLOW_STATE_READYFORRELEASE;
1461         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
1463         // Check the student can't see the grade.
1464         $this->setUser($this->students[0]);
1465         $output = $assign->view_student_summary($this->students[0], true);
1466         $this->assertEquals(false, strpos($output, '50.0'));
1468         // Mark the submission and set to released.
1469         $this->setUser($this->teachers[0]);
1470         $data = new stdClass();
1471         $data->grade = '50.0';
1472         $data->workflowstate = ASSIGN_MARKING_WORKFLOW_STATE_RELEASED;
1473         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
1475         // Check the student can see the grade.
1476         $this->setUser($this->students[0]);
1477         $output = $assign->view_student_summary($this->students[0], true);
1478         $this->assertNotEquals(false, strpos($output, '50.0'));
1479     }
1481     public function test_markerallocation() {
1482         global $PAGE;
1484         $this->setUser($this->editingteachers[0]);
1485         $assign = $this->create_instance(array('markingworkflow'=>1, 'markingallocation'=>1));
1486         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
1488         // Allocate marker to submission.
1489         $data = new stdClass();
1490         $data->allocatedmarker = $this->teachers[0]->id;
1491         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
1493         // Check the allocated marker can view the submission.
1494         $this->setUser($this->teachers[0]);
1495         $gradingtable = new assign_grading_table($assign, 100, '', 0, true);
1496         $output = $assign->get_renderer()->render($gradingtable);
1497         $this->assertEquals(true, strpos($output, $this->students[0]->lastname));
1499         // Check that other teachers can't view this submission.
1500         $this->setUser($this->teachers[1]);
1501         $gradingtable = new assign_grading_table($assign, 100, '', 0, true);
1502         $output = $assign->get_renderer()->render($gradingtable);
1503         $this->assertNotEquals(true, strpos($output, $this->students[0]->lastname));
1504     }
1508     public function test_teacher_submit_for_student() {
1509         global $PAGE;
1511         $this->preventResetByRollback();
1512         $sink = $this->redirectMessages();
1514         $this->setUser($this->editingteachers[0]);
1516         $assign = $this->create_instance(array('assignsubmission_onlinetext_enabled'=>1, 'submissiondrafts'=>1));
1517         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
1519         $this->setUser($this->students[0]);
1520         // Simulate a submission.
1521         $data = new stdClass();
1522         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
1523                                          'text'=>'Student submission text',
1524                                          'format'=>FORMAT_MOODLE);
1526         $notices = array();
1527         $assign->save_submission($data, $notices);
1529         // Check that the submission text was saved.
1530         $output = $assign->view_student_summary($this->students[0], true);
1531         $this->assertContains('Student submission text', $output, 'Contains student submission text');
1533         // Check that a teacher teacher with the extra capability can edit a students submission.
1534         $this->setUser($this->teachers[0]);
1535         $data = new stdClass();
1536         $data->userid = $this->students[0]->id;
1537         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
1538                                          'text'=>'Teacher edited submission text',
1539                                          'format'=>FORMAT_MOODLE);
1541         // Add the required capability.
1542         $roleid = create_role('Dummy role', 'dummyrole', 'dummy role description');
1543         assign_capability('mod/assign:editothersubmission', CAP_ALLOW, $roleid, $assign->get_context()->id);
1544         role_assign($roleid, $this->teachers[0]->id, $assign->get_context()->id);
1545         accesslib_clear_all_caches_for_unit_testing();
1547         // Try to save the submission.
1548         $notices = array();
1549         $assign->save_submission($data, $notices);
1551         // Check that the teacher can submit the students work.
1552         $data = new stdClass();
1553         $data->userid = $this->students[0]->id;
1554         $notices = array();
1555         $assign->submit_for_grading($data, $notices);
1557         // Revert to draft so the student can edit it.
1558         $assign->revert_to_draft($this->students[0]->id);
1560         $this->setUser($this->students[0]);
1562         // Check that the submission text was saved.
1563         $output = $assign->view_student_summary($this->students[0], true);
1564         $this->assertContains('Teacher edited submission text', $output, 'Contains student submission text');
1566         // Check that the student can submit their work.
1567         $data = new stdClass();
1568         $assign->submit_for_grading($data, $notices);
1570         $output = $assign->view_student_summary($this->students[0], true);
1571         $this->assertNotContains(get_string('addsubmission', 'assign'), $output);
1573         // Set to a default editing teacher who should not be able to edit this submission.
1574         $this->setUser($this->editingteachers[1]);
1576         // Revert to draft so the submission is editable.
1577         $assign->revert_to_draft($this->students[0]->id);
1579         $data = new stdClass();
1580         $data->userid = $this->students[0]->id;
1581         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
1582                                          'text'=>'Teacher 2 edited submission text',
1583                                          'format'=>FORMAT_MOODLE);
1585         $notices = array();
1586         $this->setExpectedException('moodle_exception');
1587         $assign->save_submission($data, $notices);
1589         $sink->close();
1590     }
1592     public function test_disable_submit_after_cutoff_date() {
1593         global $PAGE;
1595         $this->setUser($this->editingteachers[0]);
1596         $now = time();
1597         $tomorrow = $now + 24*60*60;
1598         $lastweek = $now - 7*24*60*60;
1599         $yesterday = $now - 24*60*60;
1601         $assign = $this->create_instance(array('duedate'=>$yesterday,
1602                                                'cutoffdate'=>$tomorrow,
1603                                                'assignsubmission_onlinetext_enabled'=>1));
1604         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
1606         // Student should be able to see an add submission button.
1607         $this->setUser($this->students[0]);
1608         $output = $assign->view_student_summary($this->students[0], true);
1609         $this->assertNotEquals(false, strpos($output, get_string('addsubmission', 'assign')));
1611         // Add a submission but don't submit now.
1612         $submission = $assign->get_user_submission($this->students[0]->id, true);
1613         $data = new stdClass();
1614         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
1615                                          'text'=>'Submission text',
1616                                          'format'=>FORMAT_MOODLE);
1617         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
1618         $plugin->save($submission, $data);
1620         // Create another instance with cut-off and due-date already passed.
1621         $this->setUser($this->editingteachers[0]);
1622         $now = time();
1623         $assign = $this->create_instance(array('duedate'=>$lastweek,
1624                                                'cutoffdate'=>$yesterday,
1625                                                'assignsubmission_onlinetext_enabled'=>1));
1627         $this->setUser($this->students[0]);
1628         $output = $assign->view_student_summary($this->students[0], true);
1629         $this->assertNotContains($output, get_string('editsubmission', 'assign'),
1630                                  'Should not be able to edit after cutoff date.');
1631         $this->assertNotContains($output, get_string('submitassignment', 'assign'),
1632                                  'Should not be able to submit after cutoff date.');
1633     }
1634     /**
1635      * Testing for submission comment plugin settings
1636      */
1637     public function test_submission_comment_plugin_settings() {
1638         global $CFG;
1640         $commentconfig = false;
1641         if (!empty($CFG->usecomments)) {
1642             $commentconfig = $CFG->usecomments;
1643         }
1645         $CFG->usecomments = true;
1646         $assign = $this->create_instance();
1647         $plugin = $assign->get_submission_plugin_by_type('comments');
1648         $this->assertEquals(1, $plugin->is_enabled('enabled'));
1650         $assign = $this->create_instance(array('assignsubmission_comments_enabled' => 0));
1651         $plugin = $assign->get_submission_plugin_by_type('comments');
1652         $this->assertEquals(1, $plugin->is_enabled('enabled'));
1654         $assign = $this->create_instance(array('assignsubmission_comments_enabled' => 1));
1655         $plugin = $assign->get_submission_plugin_by_type('comments');
1656         $this->assertEquals(1, $plugin->is_enabled('enabled'));
1658         $CFG->usecomments = false;
1659         $assign = $this->create_instance();
1660         $plugin = $assign->get_submission_plugin_by_type('comments');
1661         $this->assertEquals(0, $plugin->is_enabled('enabled'));
1663         $assign = $this->create_instance(array('assignsubmission_comments_enabled' => 0));
1664         $plugin = $assign->get_submission_plugin_by_type('comments');
1665         $this->assertEquals(0, $plugin->is_enabled('enabled'));
1667         $assign = $this->create_instance(array('assignsubmission_comments_enabled' => 1));
1668         $plugin = $assign->get_submission_plugin_by_type('comments');
1669         $this->assertEquals(0, $plugin->is_enabled('enabled'));
1671         $CFG->usecomments = $commentconfig;
1672     }
1674     /**
1675      * Testing for comment inline settings
1676      */
1677     public function test_feedback_comment_commentinline() {
1678         global $CFG;
1680         $sourcetext = "Hello!
1682 I'm writing to you from the Moodle Majlis in Muscat, Oman, where we just had several days of Moodle community goodness.
1684 URL outside a tag: https://moodle.org/logo/logo-240x60.gif
1685 Plugin url outside a tag: @@PLUGINFILE@@/logo-240x60.gif
1687 External link 1:<img src='https://moodle.org/logo/logo-240x60.gif' alt='Moodle'/>
1688 External link 2:<img alt=\"Moodle\" src=\"https://moodle.org/logo/logo-240x60.gif\"/>
1689 Internal link 1:<img src='@@PLUGINFILE@@/logo-240x60.gif' alt='Moodle'/>
1690 Internal link 2:<img alt=\"Moodle\" src=\"@@PLUGINFILE@@logo-240x60.gif\"/>
1691 Anchor link 1:<a href=\"@@PLUGINFILE@@logo-240x60.gif\" alt=\"bananas\">Link text</a>
1692 Anchor link 2:<a title=\"bananas\" href=\"../logo-240x60.gif\">Link text</a>
1693 ";
1695         // Note the internal images have been stripped and the html is purified (quotes fixed in this case).
1696         $filteredtext = "Hello!
1698 I'm writing to you from the Moodle Majlis in Muscat, Oman, where we just had several days of Moodle community goodness.
1700 URL outside a tag: https://moodle.org/logo/logo-240x60.gif
1701 Plugin url outside a tag: @@PLUGINFILE@@/logo-240x60.gif
1703 External link 1:<img src=\"https://moodle.org/logo/logo-240x60.gif\" alt=\"Moodle\" />
1704 External link 2:<img alt=\"Moodle\" src=\"https://moodle.org/logo/logo-240x60.gif\" />
1705 Internal link 1:
1706 Internal link 2:
1707 Anchor link 1:Link text
1708 Anchor link 2:<a title=\"bananas\" href=\"../logo-240x60.gif\">Link text</a>
1709 ";
1711         $this->setUser($this->editingteachers[0]);
1712         $params = array('assignsubmission_onlinetext_enabled' => 1,
1713                         'assignfeedback_comments_enabled' => 1,
1714                         'assignfeedback_comments_commentinline' => 1);
1715         $assign = $this->create_instance($params);
1717         $this->setUser($this->students[0]);
1718         // Add a submission but don't submit now.
1719         $submission = $assign->get_user_submission($this->students[0]->id, true);
1720         $data = new stdClass();
1722         // Test the internal link is stripped, but the external one is not.
1723         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
1724                                          'text'=>$sourcetext,
1725                                          'format'=>FORMAT_MOODLE);
1727         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
1728         $plugin->save($submission, $data);
1730         $this->setUser($this->editingteachers[0]);
1732         $data = new stdClass();
1733         require_once($CFG->dirroot . '/mod/assign/gradeform.php');
1734         $pagination = array('userid'=>$this->students[0]->id,
1735                             'rownum'=>0,
1736                             'last'=>true,
1737                             'useridlistid'=>time(),
1738                             'attemptnumber'=>0);
1739         $formparams = array($assign, $data, $pagination);
1740         $mform = new mod_assign_grade_form(null, $formparams);
1742         $this->assertEquals($filteredtext, $data->assignfeedbackcomments_editor['text']);
1743     }
1745     /**
1746      * Testing for feedback comment plugin settings
1747      */
1748     public function test_feedback_plugin_settings() {
1750         $assign = $this->create_instance();
1751         $plugin = $assign->get_feedback_plugin_by_type('comments');
1752         $this->assertEquals(0, $plugin->is_enabled('enabled'));
1754         $assign = $this->create_instance(array('assignfeedback_comments_enabled' => 0));
1755         $plugin = $assign->get_feedback_plugin_by_type('comments');
1756         $this->assertEquals(0, $plugin->is_enabled('enabled'));
1758         $assign = $this->create_instance(array('assignfeedback_comments_enabled' => 1));
1759         $plugin = $assign->get_feedback_plugin_by_type('comments');
1760         $this->assertEquals(1, $plugin->is_enabled('enabled'));
1761     }
1763     /**
1764      * Testing if gradebook feedback plugin is enabled.
1765      */
1766     public function test_is_gradebook_feedback_enabled() {
1767         $adminconfig = get_config('assign');
1768         $gradebookplugin = $adminconfig->feedback_plugin_for_gradebook;
1770         // Create assignment with gradebook feedback enabled and grade = 0.
1771         $assign = $this->create_instance(array($gradebookplugin . '_enabled' => 1, 'grades' => 0));
1773         // Get gradebook feedback plugin.
1774         $gradebookplugintype = str_replace('assignfeedback_', '', $gradebookplugin);
1775         $plugin = $assign->get_feedback_plugin_by_type($gradebookplugintype);
1776         $this->assertEquals(1, $plugin->is_enabled('enabled'));
1777         $this->assertEquals(1, $assign->is_gradebook_feedback_enabled());
1779         // Create assignment with gradebook feedback disabled and grade = 0.
1780         $assign = $this->create_instance(array($gradebookplugin . '_enabled' => 0, 'grades' => 0));
1781         $plugin = $assign->get_feedback_plugin_by_type($gradebookplugintype);
1782         $this->assertEquals(0, $plugin->is_enabled('enabled'));
1783     }
1785     /**
1786      * Testing can_edit_submission
1787      */
1788     public function test_can_edit_submission() {
1789         global $PAGE, $DB;
1790         $this->create_extra_users();
1792         $this->setAdminUser();
1793         // Create assignment (onlinetext).
1794         $assign = $this->create_instance(array('assignsubmission_onlinetext_enabled'=>1, 'submissiondrafts'=>1));
1795         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
1797         // Check student can edit their own submission.
1798         $this->assertTrue($assign->can_edit_submission($this->students[0]->id, $this->students[0]->id));
1799         // Check student cannot edit others submission.
1800         $this->assertFalse($assign->can_edit_submission($this->students[0]->id, $this->students[1]->id));
1802         // Check teacher cannot (by default) edit a students submission.
1803         $this->assertFalse($assign->can_edit_submission($this->students[0]->id, $this->teachers[0]->id));
1805         // Add the required capability to edit a student submission.
1806         $roleid = create_role('Dummy role', 'dummyrole', 'dummy role description');
1807         assign_capability('mod/assign:editothersubmission', CAP_ALLOW, $roleid, $assign->get_context()->id);
1808         role_assign($roleid, $this->teachers[0]->id, $assign->get_context()->id);
1809         accesslib_clear_all_caches_for_unit_testing();
1810         // Retest - should now have access.
1811         $this->assertTrue($assign->can_edit_submission($this->students[0]->id, $this->teachers[0]->id));
1813         // Force create an assignment with SEPARATEGROUPS.
1814         $data = new stdClass();
1815         $data->courseid = $this->course->id;
1816         $data->name = 'Grouping';
1817         $groupingid = groups_create_grouping($data);
1818         groups_assign_grouping($groupingid, $this->groups[0]->id);
1819         groups_assign_grouping($groupingid, $this->groups[1]->id);
1820         $assign = $this->create_instance(array('groupingid' => $groupingid, 'groupmode' => SEPARATEGROUPS));
1822         // Add the capability to the new assignment for extra students 0 and 1.
1823         assign_capability('mod/assign:editothersubmission', CAP_ALLOW, $roleid, $assign->get_context()->id);
1824         role_assign($roleid, $this->extrastudents[0]->id, $assign->get_context()->id);
1825         role_assign($roleid, $this->extrastudents[1]->id, $assign->get_context()->id);
1826         accesslib_clear_all_caches_for_unit_testing();
1828         // Verify the extra student does not have the capability to edit a submission not in their group.
1829         $this->assertFalse($assign->can_edit_submission($this->students[0]->id, $this->extrastudents[1]->id));
1830         // Verify the extra student does have the capability to edit a submission in their group.
1831         $this->assertTrue($assign->can_edit_submission($this->students[0]->id, $this->extrastudents[0]->id));
1833     }
1835     /**
1836      * Test if the view blind details capability works
1837      */
1838     public function test_can_view_blind_details() {
1839         global $PAGE, $DB;
1840         $teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
1841         $managerrole = $DB->get_record('role', array('shortname' => 'manager'));
1843         $student = $this->students[0];// Get a student user.
1844         // Create a teacher. Shouldn't be able to view blind marking ID.
1845         $teacher = $this->getDataGenerator()->create_user();
1847         $this->getDataGenerator()->enrol_user($teacher->id,
1848                                               $this->course->id,
1849                                               $teacherrole->id);
1851         // Create a manager.. Should be able to view blind marking ID.
1852         $manager = $this->getDataGenerator()->create_user();
1853         $this->getDataGenerator()->enrol_user($manager->id,
1854                 $this->course->id,
1855                 $managerrole->id);
1857         // Generate blind marking assignment.
1858         $assign = $this->create_instance(array('blindmarking' => 1));
1859         $this->assertEquals(true, $assign->is_blind_marking());
1861         // Test student names are hidden to teacher.
1862         $this->setUser($teacher);
1863         $gradingtable = new assign_grading_table($assign, 1, '', 0, true);
1864         $output = $assign->get_renderer()->render($gradingtable);
1865         $this->assertEquals(true, strpos($output, get_string('hiddenuser', 'assign')));    // "Participant" is somewhere on the page.
1866         $this->assertEquals(false, strpos($output, fullname($student)));    // Students full name doesn't appear.
1868         // Test student names are visible to manager.
1869         $this->setUser($manager);
1870         $gradingtable = new assign_grading_table($assign, 1, '', 0, true);
1871         $output = $assign->get_renderer()->render($gradingtable);
1872         $this->assertEquals(true, strpos($output, get_string('hiddenuser', 'assign')));
1873         $this->assertEquals(true, strpos($output, fullname($student)));    //students full name doesn't appear.
1874     }