MDL-42995 mod_assign: Remove files from calendar event description
[moodle.git] / mod / assign / tests / locallib_test.php
1 <?php
2 // This file is part of Moodle - http://moodle.org/
3 //
4 // Moodle is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // Moodle is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
17 /**
18  * Unit tests for (some of) mod/assign/locallib.php.
19  *
20  * @package    mod_assign
21  * @category   phpunit
22  * @copyright  1999 onwards Martin Dougiamas  {@link http://moodle.com}
23  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24  */
27 defined('MOODLE_INTERNAL') || die();
29 global $CFG;
30 require_once($CFG->dirroot . '/mod/assign/locallib.php');
31 require_once($CFG->dirroot . '/mod/assign/upgradelib.php');
32 require_once($CFG->dirroot . '/mod/assign/tests/base_test.php');
34 /**
35  * Unit tests for (some of) mod/assign/locallib.php.
36  *
37  * @copyright  1999 onwards Martin Dougiamas  {@link http://moodle.com}
38  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
39  */
40 class mod_assign_locallib_testcase extends mod_assign_base_testcase {
42     public function test_return_links() {
43         global $PAGE;
44         $this->setUser($this->editingteachers[0]);
45         $returnaction = 'RETURNACTION';
46         $returnparams = array('param'=>'1');
47         $assign = $this->create_instance();
48         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
49         $assign->register_return_link($returnaction, $returnparams);
50         $this->assertEquals($returnaction, $assign->get_return_action());
51         $this->assertEquals($returnparams, $assign->get_return_params());
52     }
54     public function test_get_feedback_plugins() {
55         $this->setUser($this->editingteachers[0]);
56         $assign = $this->create_instance();
57         $installedplugins = array_keys(core_component::get_plugin_list('assignfeedback'));
59         foreach ($assign->get_feedback_plugins() as $plugin) {
60             $this->assertContains($plugin->get_type(), $installedplugins, 'Feedback plugin not in list of installed plugins');
61         }
62     }
64     public function test_get_submission_plugins() {
65         $this->setUser($this->editingteachers[0]);
66         $assign = $this->create_instance();
67         $installedplugins = array_keys(core_component::get_plugin_list('assignsubmission'));
69         foreach ($assign->get_submission_plugins() as $plugin) {
70             $this->assertContains($plugin->get_type(), $installedplugins, 'Submission plugin not in list of installed plugins');
71         }
72     }
74     public function test_is_blind_marking() {
75         $this->setUser($this->editingteachers[0]);
76         $assign = $this->create_instance(array('blindmarking'=>1));
77         $this->assertEquals(true, $assign->is_blind_marking());
79         // Test cannot see student names.
80         $gradingtable = new assign_grading_table($assign, 1, '', 0, true);
81         $output = $assign->get_renderer()->render($gradingtable);
82         $this->assertEquals(true, strpos($output, get_string('hiddenuser', 'assign')));
84         // Test students cannot reveal identities.
85         $nopermission = false;
86         $this->students[0]->ignoresesskey = true;
87         $this->setUser($this->students[0]);
88         $this->setExpectedException('required_capability_exception');
89         $assign->reveal_identities();
90         $this->students[0]->ignoresesskey = false;
92         // Test teachers cannot reveal identities.
93         $nopermission = false;
94         $this->teachers[0]->ignoresesskey = true;
95         $this->setUser($this->teachers[0]);
96         $this->setExpectedException('required_capability_exception');
97         $assign->reveal_identities();
98         $this->teachers[0]->ignoresesskey = false;
100         // Test sesskey is required.
101         $this->setUser($this->editingteachers[0]);
102         $this->setExpectedException('moodle_exception');
103         $assign->reveal_identities();
105         // Test editingteacher can reveal identities if sesskey is ignored.
106         $this->editingteachers[0]->ignoresesskey = true;
107         $this->setUser($this->editingteachers[0]);
108         $assign->reveal_identities();
109         $this->assertEquals(false, $assign->is_blind_marking());
110         $this->editingteachers[0]->ignoresesskey = false;
112         // Test student names are visible.
113         $gradingtable = new assign_grading_table($assign, 1, '', 0, true);
114         $output = $assign->get_renderer()->render($gradingtable);
115         $this->assertEquals(false, strpos($output, get_string('hiddenuser', 'assign')));
117         // Set this back to default.
118         $this->editingteachers[0]->ignoresesskey = false;
119     }
121     public function test_show_intro() {
122         // Test whether we are showing the intro at the correct times.
123         $this->setUser($this->editingteachers[0]);
124         $assign = $this->create_instance(array('alwaysshowdescription'=>1));
126         $this->assertEquals(true, $assign->testable_show_intro());
128         $tomorrow = time() + (24*60*60);
130         $assign = $this->create_instance(array('alwaysshowdescription'=>0,
131                                                'allowsubmissionsfromdate'=>$tomorrow));
132         $this->assertEquals(false, $assign->testable_show_intro());
133         $yesterday = time() - (24*60*60);
134         $assign = $this->create_instance(array('alwaysshowdescription'=>0,
135                                                'allowsubmissionsfromdate'=>$yesterday));
136         $this->assertEquals(true, $assign->testable_show_intro());
137     }
139     public function test_has_submissions_or_grades() {
140         $this->setUser($this->editingteachers[0]);
141         $assign = $this->create_instance(array('assignsubmission_onlinetext_enabled'=>1));
143         $instance = $assign->get_instance();
145         // Should start empty.
146         $this->assertEquals(false, $assign->has_submissions_or_grades());
148         // Simulate a submission.
149         $this->setUser($this->students[0]);
150         $submission = $assign->get_user_submission($this->students[0]->id, true);
151         $data = new stdClass();
152         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
153                                          'text'=>'Submission text',
154                                          'format'=>FORMAT_MOODLE);
155         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
156         $plugin->save($submission, $data);
158         // Now test again.
159         $this->assertEquals(true, $assign->has_submissions_or_grades());
160         // Set this back to default.
161         $this->students[0]->ignoresesskey = false;
162     }
164     public function test_delete_grades() {
165         $this->setUser($this->editingteachers[0]);
166         $assign = $this->create_instance();
168         // Simulate adding a grade.
169         $this->setUser($this->teachers[0]);
170         $data = new stdClass();
171         $data->grade = '50.0';
172         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
174         // Now see if the data is in the gradebook.
175         $gradinginfo = grade_get_grades($this->course->id,
176                                         'mod',
177                                         'assign',
178                                         $assign->get_instance()->id);
180         $this->assertNotEquals(0, count($gradinginfo->items));
182         $assign->testable_delete_grades();
183         $gradinginfo = grade_get_grades($this->course->id,
184                                         'mod',
185                                         'assign',
186                                         $assign->get_instance()->id);
188         $this->assertEquals(0, count($gradinginfo->items));
189     }
191     public function test_delete_instance() {
192         $this->setUser($this->editingteachers[0]);
193         $assign = $this->create_instance(array('assignsubmission_onlinetext_enabled'=>1));
195         // Simulate adding a grade.
196         $this->setUser($this->teachers[0]);
197         $data = new stdClass();
198         $data->grade = '50.0';
199         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
201         // Simulate a submission.
202         $this->setUser($this->students[0]);
203         $submission = $assign->get_user_submission($this->students[0]->id, true);
204         $data = new stdClass();
205         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
206                                          'text'=>'Submission text',
207                                          'format'=>FORMAT_MOODLE);
208         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
209         $plugin->save($submission, $data);
211         // Now try and delete.
212         $this->assertEquals(true, $assign->delete_instance());
213     }
215     public function test_reset_userdata() {
216         global $DB;
218         $now = time();
219         $this->setUser($this->editingteachers[0]);
220         $assign = $this->create_instance(array('assignsubmission_onlinetext_enabled'=>1,
221                                                'duedate'=>$now));
223         // Simulate adding a grade.
224         $this->setUser($this->teachers[0]);
225         $data = new stdClass();
226         $data->grade = '50.0';
227         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
229         // Simulate a submission.
230         $this->setUser($this->students[0]);
231         $submission = $assign->get_user_submission($this->students[0]->id, true);
232         $data = new stdClass();
233         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
234                                          'text'=>'Submission text',
235                                          'format'=>FORMAT_MOODLE);
236         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
237         $plugin->save($submission, $data);
239         $this->assertEquals(true, $assign->has_submissions_or_grades());
240         // Now try and reset.
241         $data = new stdClass();
242         $data->reset_assign_submissions = 1;
243         $data->reset_gradebook_grades = 1;
244         $data->courseid = $this->course->id;
245         $data->timeshift = 24*60*60;
246         $this->setUser($this->editingteachers[0]);
247         $assign->reset_userdata($data);
248         $this->assertEquals(false, $assign->has_submissions_or_grades());
250         // Reload the instance data.
251         $instance = $DB->get_record('assign', array('id'=>$assign->get_instance()->id));
252         $this->assertEquals($now + 24*60*60, $instance->duedate);
254         // Test reset using assign_reset_userdata().
255         $assignduedate = $instance->duedate; // Keep old updated value for comparison.
256         $data->timeshift = 2*24*60*60;
257         assign_reset_userdata($data);
258         $instance = $DB->get_record('assign', array('id' => $assign->get_instance()->id));
259         $this->assertEquals($assignduedate + 2*24*60*60, $instance->duedate);
261         // Create one more assignment and reset, make sure time shifted for previous assignment is not changed.
262         $assign2 = $this->create_instance(array('assignsubmission_onlinetext_enabled' => 1,
263                                                'duedate' => $now));
264         $assignduedate = $instance->duedate;
265         $data->timeshift = 3*24*60*60;
266         $assign2->reset_userdata($data);
267         $instance = $DB->get_record('assign', array('id' => $assign->get_instance()->id));
268         $this->assertEquals($assignduedate, $instance->duedate);
269         $instance2 = $DB->get_record('assign', array('id' => $assign2->get_instance()->id));
270         $this->assertEquals($now + 3*24*60*60, $instance2->duedate);
272         // Reset both assignments using assign_reset_userdata() and make sure both assignments have same date.
273         $assignduedate = $instance->duedate;
274         $assign2duedate = $instance2->duedate;
275         $data->timeshift = 4*24*60*60;
276         assign_reset_userdata($data);
277         $instance = $DB->get_record('assign', array('id' => $assign->get_instance()->id));
278         $this->assertEquals($assignduedate + 4*24*60*60, $instance->duedate);
279         $instance2 = $DB->get_record('assign', array('id' => $assign2->get_instance()->id));
280         $this->assertEquals($assign2duedate + 4*24*60*60, $instance2->duedate);
281     }
283     public function test_plugin_settings() {
284         global $DB;
286         $now = time();
287         $this->setUser($this->editingteachers[0]);
288         $assign = $this->create_instance(array('assignsubmission_file_enabled'=>1,
289                                                'assignsubmission_file_maxfiles'=>12,
290                                                'assignsubmission_file_maxsizebytes'=>10));
292         $plugin = $assign->get_submission_plugin_by_type('file');
293         $this->assertEquals('12', $plugin->get_config('maxfilesubmissions'));
294     }
296     public function test_update_calendar() {
297         global $DB;
299         $now = time();
300         $this->setUser($this->editingteachers[0]);
301         $assign = $this->create_instance(array('duedate'=>$now));
303         // See if there is an event in the calendar.
304         $params = array('modulename'=>'assign', 'instance'=>$assign->get_instance()->id);
305         $id = $DB->get_field('event', 'id', $params);
307         $this->assertEquals(false, empty($id));
308     }
310     public function test_update_instance() {
311         global $DB;
313         $this->setUser($this->editingteachers[0]);
314         $assign = $this->create_instance(array('assignsubmission_onlinetext_enabled'=>1));
316         $now = time();
317         $instance = $assign->get_instance();
318         $instance->duedate = $now;
319         $instance->instance = $instance->id;
320         $instance->assignsubmission_onlinetext_enabled = 1;
322         $assign->update_instance($instance);
324         $instance = $DB->get_record('assign', array('id'=>$assign->get_instance()->id));
325         $this->assertEquals($now, $instance->duedate);
326     }
328     public function test_cannot_submit_empty() {
329         global $PAGE;
331         $this->setUser($this->editingteachers[0]);
332         $assign = $this->create_instance(array('submissiondrafts'=>1));
334         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
336         // Test you cannot see the submit button for an offline assignment regardless.
337         $this->setUser($this->students[0]);
338         $output = $assign->view_student_summary($this->students[0], true);
339         $this->assertNotContains(get_string('submitassignment', 'assign'), $output, 'Can submit empty offline assignment');
341         // Test you cannot see the submit button for an online text assignment with no submission.
342         $this->setUser($this->editingteachers[0]);
343         $instance = $assign->get_instance();
344         $instance->instance = $instance->id;
345         $instance->assignsubmission_onlinetext_enabled = 1;
347         $assign->update_instance($instance);
348         $this->setUser($this->students[0]);
349         $output = $assign->view_student_summary($this->students[0], true);
350         $this->assertNotContains(get_string('submitassignment', 'assign'), $output, 'Cannot submit empty onlinetext assignment');
352         // Simulate a submission.
353         $submission = $assign->get_user_submission($this->students[0]->id, true);
354         $data = new stdClass();
355         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
356                                          'text'=>'Submission text',
357                                          'format'=>FORMAT_MOODLE);
358         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
359         $plugin->save($submission, $data);
360         // Test you can see the submit button for an online text assignment with a submission.
361         $output = $assign->view_student_summary($this->students[0], true);
362         $this->assertContains(get_string('submitassignment', 'assign'), $output, 'Can submit non empty onlinetext assignment');
363     }
365     public function test_list_participants() {
366         $this->create_extra_users();
367         $this->setUser($this->editingteachers[0]);
368         $assign = $this->create_instance(array('grade'=>100));
370         $this->assertEquals(self::DEFAULT_STUDENT_COUNT + self::EXTRA_STUDENT_COUNT, count($assign->list_participants(null, true)));
372         // Teacher with user preference set should see suspended users as well.
373         set_user_preference('grade_report_showonlyactiveenrol', false);
374         $assign = $this->create_instance(array('grade'=>100));
375         $this->assertEquals(self::DEFAULT_STUDENT_COUNT + self::EXTRA_STUDENT_COUNT + self::EXTRA_SUSPENDED_COUNT,
376                 count($assign->list_participants(null, true)));
378         // Non-editing teacher should not see suspended users, even if user preference is set.
379         $this->setUser($this->teachers[0]);
380         set_user_preference('grade_report_showonlyactiveenrol', false);
381         $assign = $this->create_instance(array('grade'=>100));
382         $this->assertEquals(self::DEFAULT_STUDENT_COUNT + self::EXTRA_STUDENT_COUNT, count($assign->list_participants(null, true)));
383     }
385     public function test_count_teams() {
386         $this->create_extra_users();
387         $this->setUser($this->editingteachers[0]);
388         $assign = $this->create_instance(array('teamsubmission'=>1));
390         $this->assertEquals(self::GROUP_COUNT + 1, $assign->count_teams());
391     }
393     public function test_count_submissions() {
394         $this->create_extra_users();
395         $this->setUser($this->editingteachers[0]);
396         $assign = $this->create_instance(array('assignsubmission_onlinetext_enabled'=>1));
398         // Simulate a submission.
399         $this->setUser($this->extrastudents[0]);
400         $submission = $assign->get_user_submission($this->extrastudents[0]->id, true);
401         // Leave this one as DRAFT.
402         $data = new stdClass();
403         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
404                                          'text'=>'Submission text',
405                                          'format'=>FORMAT_MOODLE);
406         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
407         $plugin->save($submission, $data);
409         // Simulate adding a grade.
410         $this->setUser($this->teachers[0]);
411         $data = new stdClass();
412         $data->grade = '50.0';
413         $assign->testable_apply_grade_to_user($data, $this->extrastudents[0]->id, 0);
415         // Simulate a submission.
416         $this->setUser($this->extrastudents[1]);
417         $submission = $assign->get_user_submission($this->extrastudents[1]->id, true);
418         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
419         $assign->testable_update_submission($submission, $this->extrastudents[1]->id, true, false);
420         $data = new stdClass();
421         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
422                                          'text'=>'Submission text',
423                                          'format'=>FORMAT_MOODLE);
424         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
425         $plugin->save($submission, $data);
427         // Simulate a submission.
428         $this->setUser($this->extrastudents[2]);
429         $submission = $assign->get_user_submission($this->extrastudents[2]->id, true);
430         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
431         $assign->testable_update_submission($submission, $this->extrastudents[2]->id, true, false);
432         $data = new stdClass();
433         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
434                                          'text'=>'Submission text',
435                                          'format'=>FORMAT_MOODLE);
436         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
437         $plugin->save($submission, $data);
439         // Simulate a submission.
440         $this->setUser($this->extrastudents[3]);
441         $submission = $assign->get_user_submission($this->extrastudents[3]->id, true);
442         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
443         $assign->testable_update_submission($submission, $this->extrastudents[3]->id, true, false);
444         $data = new stdClass();
445         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
446                                          'text'=>'Submission text',
447                                          'format'=>FORMAT_MOODLE);
448         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
449         $plugin->save($submission, $data);
451         // Simulate a submission for suspended user, this will never be counted.
452         $this->setUser($this->extrastudents[3]);
453         $submission = $assign->get_user_submission($this->extrasuspendedstudents[0]->id, true);
454         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
455         $assign->testable_update_submission($submission, $this->extrasuspendedstudents[0]->id, true, false);
456         $data = new stdClass();
457         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
458                                          'text'=>'Submission text',
459                                          'format'=>FORMAT_MOODLE);
460         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
461         $plugin->save($submission, $data);
463         // Simulate adding a grade.
464         $this->setUser($this->teachers[0]);
465         $data = new stdClass();
466         $data->grade = '50.0';
467         $assign->testable_apply_grade_to_user($data, $this->extrastudents[3]->id, 0);
468         $assign->testable_apply_grade_to_user($data, $this->extrasuspendedstudents[0]->id, 0);
470         $this->assertEquals(2, $assign->count_grades());
471         $this->assertEquals(4, $assign->count_submissions());
472         $this->assertEquals(2, $assign->count_submissions_need_grading());
473         $this->assertEquals(3, $assign->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_SUBMITTED));
474         $this->assertEquals(1, $assign->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_DRAFT));
475     }
477     public function test_get_grading_userid_list() {
478         $this->create_extra_users();
479         $this->setUser($this->editingteachers[0]);
480         $assign = $this->create_instance();
482         $users = $assign->testable_get_grading_userid_list();
483         $this->assertEquals(self::DEFAULT_STUDENT_COUNT + self::EXTRA_STUDENT_COUNT, count($users));
485         $this->setUser($this->editingteachers[0]);
486         set_user_preference('grade_report_showonlyactiveenrol', false);
487         $assign = $this->create_instance();
489         $users = $assign->testable_get_grading_userid_list();
490         $this->assertEquals(self::DEFAULT_STUDENT_COUNT + self::EXTRA_STUDENT_COUNT + self::EXTRA_SUSPENDED_COUNT, count($users));
491     }
493     public function test_cron() {
494         // First run cron so there are no messages waiting to be sent (from other tests).
495         cron_setup_user();
496         assign::cron();
498         // Now create an assignment and add some feedback.
499         $this->setUser($this->editingteachers[0]);
500         $assign = $this->create_instance();
502         // Simulate adding a grade.
503         $this->setUser($this->teachers[0]);
504         $data = new stdClass();
505         $data->grade = '50.0';
506         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
507         $assign->testable_apply_grade_to_user($data, $this->students[1]->id, 0);
509         // Now run cron and see that one message was sent.
510         $this->preventResetByRollback();
511         $sink = $this->redirectMessages();
512         cron_setup_user();
513         $this->expectOutputRegex('/Done processing 2 assignment submissions/');
514         assign::cron();
516         $messages = $sink->get_messages();
517         $this->assertEquals(2, count($messages));
518         $this->assertEquals(1, $messages[0]->notification);
519         $this->assertEquals($assign->get_instance()->name, $messages[0]->contexturlname);
520     }
522     public function test_is_graded() {
523         $this->setUser($this->editingteachers[0]);
524         $assign = $this->create_instance();
526         // Simulate adding a grade.
527         $this->setUser($this->teachers[0]);
528         $data = new stdClass();
529         $data->grade = '50.0';
530         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
532         $this->assertEquals(true, $assign->testable_is_graded($this->students[0]->id));
533         $this->assertEquals(false, $assign->testable_is_graded($this->students[1]->id));
534     }
536     public function test_can_view_submission() {
537         $this->create_extra_users();
538         $this->setUser($this->editingteachers[0]);
539         $assign = $this->create_instance();
541         $this->setUser($this->students[0]);
542         $this->assertEquals(true, $assign->can_view_submission($this->students[0]->id));
543         $this->assertEquals(false, $assign->can_view_submission($this->students[1]->id));
544         $this->assertEquals(false, $assign->can_view_submission($this->teachers[0]->id));
545         $this->setUser($this->teachers[0]);
546         $this->assertEquals(true, $assign->can_view_submission($this->students[0]->id));
547         $this->assertEquals(true, $assign->can_view_submission($this->students[1]->id));
548         $this->assertEquals(true, $assign->can_view_submission($this->teachers[0]->id));
549         $this->assertEquals(false, $assign->can_view_submission($this->extrasuspendedstudents[0]->id));
550         $this->setUser($this->editingteachers[0]);
551         $this->assertEquals(true, $assign->can_view_submission($this->students[0]->id));
552         $this->assertEquals(true, $assign->can_view_submission($this->students[1]->id));
553         $this->assertEquals(true, $assign->can_view_submission($this->teachers[0]->id));
554         $this->assertEquals(true, $assign->can_view_submission($this->extrasuspendedstudents[0]->id));
555     }
558     public function test_update_submission() {
559         $this->create_extra_users();
560         $this->setUser($this->editingteachers[0]);
561         $assign = $this->create_instance();
563         $this->setUser($this->extrastudents[0]);
564         $now = time();
565         $submission = $assign->get_user_submission($this->extrastudents[0]->id, true);
566         $assign->testable_update_submission($submission, $this->extrastudents[0]->id, true, false);
568         $this->setUser($this->teachers[0]);
569         // Verify the gradebook update.
570         $gradinginfo = grade_get_grades($this->course->id,
571                                         'mod',
572                                         'assign',
573                                         $assign->get_instance()->id,
574                                         $this->extrastudents[0]->id);
576         $this->assertEquals($this->extrastudents[0]->id,
577                             $gradinginfo->items[0]->grades[$this->extrastudents[0]->id]->usermodified);
579         // Now verify group assignments.
580         $this->setUser($this->editingteachers[0]);
581         $assign = $this->create_instance(array('teamsubmission'=>1));
583         $this->setUser($this->extrastudents[0]);
584         $now = time();
585         $submission = $assign->get_group_submission($this->extrastudents[0]->id, 0, true);
586         $assign->testable_update_submission($submission, $this->extrastudents[0]->id, true, true);
588         // Check that at least 2 active members and 1 suspended member of the submission group had their submission updated.
590         $this->setUser($this->editingteachers[0]);
591         $gradinginfo = grade_get_grades($this->course->id,
592                                         'mod',
593                                         'assign',
594                                         $assign->get_instance()->id,
595                                         $this->extrastudents[0]->id);
597         $this->assertEquals($this->extrastudents[0]->id,
598                             $gradinginfo->items[0]->grades[$this->extrastudents[0]->id]->usermodified);
600         $gradinginfo = grade_get_grades($this->course->id,
601                                         'mod',
602                                         'assign',
603                                         $assign->get_instance()->id,
604                                         $this->extrastudents[self::GROUP_COUNT]->id);
606         $this->assertEquals($this->extrastudents[self::GROUP_COUNT]->id,
607                             $gradinginfo->items[0]->grades[$this->extrastudents[self::GROUP_COUNT]->id]->usermodified);
609         $gradinginfo = grade_get_grades($this->course->id,
610                                         'mod',
611                                         'assign',
612                                         $assign->get_instance()->id,
613                                         $this->extrasuspendedstudents[0]->id);
614         $this->assertEquals($this->extrasuspendedstudents[0]->id,
615                             $gradinginfo->items[0]->grades[$this->extrasuspendedstudents[0]->id]->usermodified);
617         // Check the same with non-editing teacher and make sure submission is not updated for suspended user.
618         $this->setUser($this->editingteachers[0]);
619         $assign = $this->create_instance(array('teamsubmission'=>1));
621         $this->setUser($this->extrastudents[1]);
622         $now = time();
623         $submission = $assign->get_group_submission($this->extrastudents[1]->id, 0, true);
624         $assign->testable_update_submission($submission, $this->extrastudents[1]->id, true, true);
626         $this->setUser($this->teachers[0]);
627         $gradinginfo = grade_get_grades($this->course->id,
628                                         'mod',
629                                         'assign',
630                                         $assign->get_instance()->id,
631                                         $this->extrastudents[1]->id);
633         $this->assertEquals($this->extrastudents[1]->id,
634                             $gradinginfo->items[0]->grades[$this->extrastudents[1]->id]->usermodified);
636         $gradinginfo = grade_get_grades($this->course->id,
637                                         'mod',
638                                         'assign',
639                                         $assign->get_instance()->id,
640                                         $this->extrastudents[self::GROUP_COUNT+1]->id);
642         $this->assertEquals($this->extrastudents[self::GROUP_COUNT+1]->id,
643                             $gradinginfo->items[0]->grades[$this->extrastudents[self::GROUP_COUNT+1]->id]->usermodified);
645         $gradinginfo = grade_get_grades($this->course->id,
646                                         'mod',
647                                         'assign',
648                                         $assign->get_instance()->id,
649                                         $this->extrasuspendedstudents[1]->id);
650         $this->assertEquals($this->extrasuspendedstudents[1]->id,
651                             $gradinginfo->items[0]->grades[$this->extrasuspendedstudents[1]->id]->usermodified);
653         // Now verify blind marking.
654         $this->setUser($this->editingteachers[0]);
655         $assign = $this->create_instance(array('blindmarking'=>1));
657         $this->setUser($this->extrastudents[0]);
658         $now = time();
659         $submission = $assign->get_user_submission($this->extrastudents[0]->id, true);
660         $assign->testable_update_submission($submission, $this->extrastudents[0]->id, true, false);
662         $this->setUser($this->editingteachers[0]);
663         $gradinginfo = grade_get_grades($this->course->id,
664                                         'mod',
665                                         'assign',
666                                         $assign->get_instance()->id,
667                                         $this->extrastudents[0]->id);
669         $this->assertEquals(null, $gradinginfo->items[0]->grades[$this->extrastudents[0]->id]->datesubmitted);
670     }
672     public function test_group_submissions_submit_for_marking() {
673         global $PAGE;
675         $this->create_extra_users();
676         // Now verify group assignments.
677         $this->setUser($this->editingteachers[0]);
678         $assign = $this->create_instance(array('teamsubmission'=>1,
679                                                'assignsubmission_onlinetext_enabled'=>1,
680                                                'submissiondrafts'=>1,
681                                                'requireallteammemberssubmit'=>1));
682         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
684         $this->setUser($this->extrastudents[0]);
685         // Add a submission.
686         $data = new stdClass();
687         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
688                                          'text'=>'Submission text',
689                                          'format'=>FORMAT_MOODLE);
691         $notices = array();
692         $assign->save_submission($data, $notices);
694         // Check we can see the submit button.
695         $output = $assign->view_student_summary($this->extrastudents[0], true);
696         $this->assertContains(get_string('submitassignment', 'assign'), $output);
698         $submission = $assign->get_group_submission($this->extrastudents[0]->id, 0, true);
699         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
700         $assign->testable_update_submission($submission, $this->extrastudents[0]->id, true, true);
702         // Check that the student does not see "Submit" button.
703         $output = $assign->view_student_summary($this->extrastudents[0], true);
704         $this->assertNotContains(get_string('submitassignment', 'assign'), $output);
706         // Change to another user in the same group.
707         $this->setUser($this->extrastudents[self::GROUP_COUNT]);
708         $output = $assign->view_student_summary($this->extrastudents[self::GROUP_COUNT], true);
709         $this->assertContains(get_string('submitassignment', 'assign'), $output);
711         $submission = $assign->get_group_submission($this->extrastudents[self::GROUP_COUNT]->id, 0, true);
712         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
713         $assign->testable_update_submission($submission, $this->extrastudents[self::GROUP_COUNT]->id, true, true);
714         $output = $assign->view_student_summary($this->extrastudents[self::GROUP_COUNT], true);
715         $this->assertNotContains(get_string('submitassignment', 'assign'), $output);
716     }
718     public function test_submissions_open() {
719         $this->setUser($this->editingteachers[0]);
721         $now = time();
722         $tomorrow = $now + 24*60*60;
723         $oneweek = $now + 7*24*60*60;
724         $yesterday = $now - 24*60*60;
726         $assign = $this->create_instance();
727         $this->assertEquals(true, $assign->testable_submissions_open($this->students[0]->id));
729         $assign = $this->create_instance(array('duedate'=>$tomorrow));
730         $this->assertEquals(true, $assign->testable_submissions_open($this->students[0]->id));
732         $assign = $this->create_instance(array('duedate'=>$yesterday));
733         $this->assertEquals(true, $assign->testable_submissions_open($this->students[0]->id));
735         $assign = $this->create_instance(array('duedate'=>$yesterday, 'cutoffdate'=>$tomorrow));
736         $this->assertEquals(true, $assign->testable_submissions_open($this->students[0]->id));
738         $assign = $this->create_instance(array('duedate'=>$yesterday, 'cutoffdate'=>$yesterday));
739         $this->assertEquals(false, $assign->testable_submissions_open($this->students[0]->id));
741         $assign->testable_save_user_extension($this->students[0]->id, $tomorrow);
742         $this->assertEquals(true, $assign->testable_submissions_open($this->students[0]->id));
744         $assign = $this->create_instance(array('submissiondrafts'=>1));
745         $this->assertEquals(true, $assign->testable_submissions_open($this->students[0]->id));
747         $this->setUser($this->students[0]);
748         $now = time();
749         $submission = $assign->get_user_submission($this->students[0]->id, true);
750         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
751         $assign->testable_update_submission($submission, $this->students[0]->id, true, false);
752         $this->setUser($this->editingteachers[0]);
753         $this->assertEquals(false, $assign->testable_submissions_open($this->students[0]->id));
754     }
756     public function test_get_graders() {
757         $this->create_extra_users();
758         $this->setUser($this->editingteachers[0]);
760         // Create an assignment with no groups.
761         $assign = $this->create_instance();
762         $this->assertCount(self::DEFAULT_TEACHER_COUNT +
763                            self::DEFAULT_EDITING_TEACHER_COUNT +
764                            self::EXTRA_TEACHER_COUNT +
765                            self::EXTRA_EDITING_TEACHER_COUNT,
766                            $assign->testable_get_graders($this->students[0]->id));
768         // Force create an assignment with SEPARATEGROUPS.
769         $data = new stdClass();
770         $data->courseid = $this->course->id;
771         $data->name = 'Grouping';
772         $groupingid = groups_create_grouping($data);
773         groups_assign_grouping($groupingid, $this->groups[0]->id);
774         $assign = $this->create_instance(array('groupingid' => $groupingid, 'groupmode' => SEPARATEGROUPS));
776         $this->setUser($this->students[1]);
777         $this->assertCount(4, $assign->testable_get_graders($this->students[0]->id));
778         // Note the second student is in a group that is not in the grouping.
779         // This means that we get all graders that are not in a group in the grouping.
780         $this->assertCount(10, $assign->testable_get_graders($this->students[1]->id));
781     }
783     public function test_group_members_only() {
784         global $CFG;
786         $this->setAdminUser();
787         $this->create_extra_users();
788         $CFG->enablegroupmembersonly = true;
789         $grouping = $this->getDataGenerator()->create_grouping(array('courseid' => $this->course->id));
790         groups_assign_grouping($grouping->id, $this->groups[0]->id);
792         // Force create an assignment with SEPARATEGROUPS.
793         $instance = $this->getDataGenerator()->create_module('assign', array('course'=>$this->course->id),
794             array('groupmembersonly' => SEPARATEGROUPS, 'groupingid' => $grouping->id));
796         $cm = get_coursemodule_from_instance('assign', $instance->id);
797         $context = context_module::instance($cm->id);
798         $assign = new testable_assign($context, $cm, $this->course);
800         $this->setUser($this->teachers[0]);
801         $this->assertCount(5, $assign->list_participants(0, true));
803     }
805     public function test_get_uniqueid_for_user() {
806         $this->setUser($this->editingteachers[0]);
807         $assign = $this->create_instance();
809         foreach ($this->students as $student) {
810             $uniqueid = $assign->get_uniqueid_for_user($student->id);
811             $this->assertEquals($student->id, $assign->get_user_id_for_uniqueid($uniqueid));
812         }
813     }
815     public function test_show_student_summary() {
816         global $CFG, $PAGE;
818         $this->setUser($this->editingteachers[0]);
819         $assign = $this->create_instance();
820         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
822         // No feedback should be available because this student has not been graded.
823         $this->setUser($this->students[0]);
824         $output = $assign->view_student_summary($this->students[0], true);
825         $this->assertEquals(false, strpos($output, 'Feedback'), 'Do not show feedback if there is no grade');
826         // Simulate adding a grade.
827         $this->setUser($this->teachers[0]);
828         $data = new stdClass();
829         $data->grade = '50.0';
830         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
832         // Now we should see the feedback.
833         $this->setUser($this->students[0]);
834         $output = $assign->view_student_summary($this->students[0], true);
835         $this->assertNotEquals(false, strpos($output, 'Feedback'), 'Show feedback if there is a grade');
837         // Now hide the grade in gradebook.
838         $this->setUser($this->teachers[0]);
839         require_once($CFG->libdir.'/gradelib.php');
840         $gradeitem = new grade_item(array(
841             'itemtype'      => 'mod',
842             'itemmodule'    => 'assign',
843             'iteminstance'  => $assign->get_instance()->id,
844             'courseid'      => $this->course->id));
846         $gradeitem->set_hidden(1, false);
848         // No feedback should be available because the grade is hidden.
849         $this->setUser($this->students[0]);
850         $output = $assign->view_student_summary($this->students[0], true);
851         $this->assertEquals(false, strpos($output, 'Feedback'), 'Do not show feedback if the grade is hidden in the gradebook');
853         // Do the same but add feedback.
854         $assign = $this->create_instance(array('assignfeedback_comments_enabled' => 1));
856         $this->setUser($this->teachers[0]);
857         $grade = $assign->get_user_grade($this->students[0]->id, true);
858         $data = new stdClass();
859         $data->assignfeedbackcomments_editor = array('text'=>'Tomato sauce',
860                                          'format'=>FORMAT_MOODLE);
861         $plugin = $assign->get_feedback_plugin_by_type('comments');
862         $plugin->save($grade, $data);
864         // Should have feedback but no grade.
865         $this->setUser($this->students[0]);
866         $output = $assign->view_student_summary($this->students[0], true);
867         $this->assertNotEquals(false, strpos($output, 'Feedback'), 'Show feedback even if there is no grade');
868         $this->assertEquals(false, strpos($output, 'Grade'), 'Do not show grade when there is no grade.');
869         $this->assertEquals(false, strpos($output, 'Graded on'), 'Do not show graded date when there is no grade.');
871         // Now hide the grade in gradebook.
872         $this->setUser($this->teachers[0]);
873         $gradeitem = new grade_item(array(
874             'itemtype'      => 'mod',
875             'itemmodule'    => 'assign',
876             'iteminstance'  => $assign->get_instance()->id,
877             'courseid'      => $this->course->id));
879         $gradeitem->set_hidden(1, false);
881         // No feedback should be available because the grade is hidden.
882         $this->setUser($this->students[0]);
883         $output = $assign->view_student_summary($this->students[0], true);
884         $this->assertEquals(false, strpos($output, 'Feedback'), 'Do not show feedback if the grade is hidden in the gradebook');
885     }
887     public function test_attempt_reopen_method_manual() {
888         global $PAGE;
890         $this->setUser($this->editingteachers[0]);
891         $assign = $this->create_instance(array('attemptreopenmethod'=>ASSIGN_ATTEMPT_REOPEN_METHOD_MANUAL,
892                                                'maxattempts'=>3,
893                                                'submissiondrafts'=>1,
894                                                'assignsubmission_onlinetext_enabled'=>1));
895         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
897         // Student should be able to see an add submission button.
898         $this->setUser($this->students[0]);
899         $output = $assign->view_student_summary($this->students[0], true);
900         $this->assertNotEquals(false, strpos($output, get_string('addsubmission', 'assign')));
902         // Add a submission.
903         $now = time();
904         $submission = $assign->get_user_submission($this->students[0]->id, true);
905         $data = new stdClass();
906         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
907                                          'text'=>'Submission text',
908                                          'format'=>FORMAT_MOODLE);
909         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
910         $plugin->save($submission, $data);
912         // And now submit it for marking.
913         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
914         $assign->testable_update_submission($submission, $this->students[0]->id, true, false);
916         // Verify the student cannot make changes to the submission.
917         $output = $assign->view_student_summary($this->students[0], true);
918         $this->assertEquals(false, strpos($output, get_string('addsubmission', 'assign')));
920         // Mark the submission.
921         $this->setUser($this->teachers[0]);
922         $data = new stdClass();
923         $data->grade = '50.0';
924         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
926         // Check the student can see the grade.
927         $this->setUser($this->students[0]);
928         $output = $assign->view_student_summary($this->students[0], true);
929         $this->assertNotEquals(false, strpos($output, '50.0'));
931         // Allow the student another attempt.
932         $this->teachers[0]->ignoresesskey = true;
933         $this->setUser($this->teachers[0]);
934         $result = $assign->testable_process_add_attempt($this->students[0]->id);
935         $this->assertEquals(true, $result);
937         // Check that the previous attempt is now in the submission history table.
938         $this->setUser($this->students[0]);
939         $output = $assign->view_student_summary($this->students[0], true);
940         // Need a better check.
941         $this->assertNotEquals(false, strpos($output, 'Submission text'), 'Contains: Submission text');
943         // Check that the student now has a button for Add a new attempt".
944         $this->assertNotEquals(false, strpos($output, get_string('addnewattempt', 'assign')));
945         // Check that the student now does not have a button for Submit.
946         $this->assertEquals(false, strpos($output, get_string('submitassignment', 'assign')));
948         // Check that the student now has a submission history.
949         $this->assertNotEquals(false, strpos($output, get_string('attempthistory', 'assign')));
951         $this->setUser($this->teachers[0]);
952         // Check that the grading table loads correctly and contains this user.
953         // This is also testing that we do not get duplicate rows in the grading table.
954         $gradingtable = new assign_grading_table($assign, 100, '', 0, true);
955         $output = $assign->get_renderer()->render($gradingtable);
956         $this->assertEquals(true, strpos($output, $this->students[0]->lastname));
958         // Should be 1 not 2.
959         $this->assertEquals(1, $assign->count_submissions());
960         $this->assertEquals(1, $assign->count_submissions_with_status('reopened'));
961         $this->assertEquals(0, $assign->count_submissions_need_grading());
962         $this->assertEquals(1, $assign->count_grades());
964         // Change max attempts to unlimited.
965         $formdata = clone($assign->get_instance());
966         $formdata->maxattempts = ASSIGN_UNLIMITED_ATTEMPTS;
967         $formdata->instance = $formdata->id;
968         $assign->update_instance($formdata);
970         // Check we can repopen still.
971         $result = $assign->testable_process_add_attempt($this->students[0]->id);
972         $this->assertEquals(true, $result);
974         $grades = $assign->get_user_grades_for_gradebook($this->students[0]->id);
975         $this->assertEquals(50, (int)$grades[$this->students[0]->id]->rawgrade);
977     }
979     public function test_markingworkflow() {
980         global $PAGE;
982         $this->setUser($this->editingteachers[0]);
983         $assign = $this->create_instance(array('markingworkflow'=>1));
984         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
986         // Mark the submission and set to notmarked.
987         $this->setUser($this->teachers[0]);
988         $data = new stdClass();
989         $data->grade = '50.0';
990         $data->workflowstate = ASSIGN_MARKING_WORKFLOW_STATE_NOTMARKED;
991         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
993         // Check the student can't see the grade.
994         $this->setUser($this->students[0]);
995         $output = $assign->view_student_summary($this->students[0], true);
996         $this->assertEquals(false, strpos($output, '50.0'));
998         // Mark the submission and set to inmarking.
999         $this->setUser($this->teachers[0]);
1000         $data = new stdClass();
1001         $data->grade = '50.0';
1002         $data->workflowstate = ASSIGN_MARKING_WORKFLOW_STATE_INMARKING;
1003         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
1005         // Check the student can't see the grade.
1006         $this->setUser($this->students[0]);
1007         $output = $assign->view_student_summary($this->students[0], true);
1008         $this->assertEquals(false, strpos($output, '50.0'));
1010         // Mark the submission and set to readyforreview.
1011         $this->setUser($this->teachers[0]);
1012         $data = new stdClass();
1013         $data->grade = '50.0';
1014         $data->workflowstate = ASSIGN_MARKING_WORKFLOW_STATE_READYFORREVIEW;
1015         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
1017         // Check the student can't see the grade.
1018         $this->setUser($this->students[0]);
1019         $output = $assign->view_student_summary($this->students[0], true);
1020         $this->assertEquals(false, strpos($output, '50.0'));
1022         // Mark the submission and set to inreview.
1023         $this->setUser($this->teachers[0]);
1024         $data = new stdClass();
1025         $data->grade = '50.0';
1026         $data->workflowstate = ASSIGN_MARKING_WORKFLOW_STATE_INREVIEW;
1027         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
1029         // Check the student can't see the grade.
1030         $this->setUser($this->students[0]);
1031         $output = $assign->view_student_summary($this->students[0], true);
1032         $this->assertEquals(false, strpos($output, '50.0'));
1034         // Mark the submission and set to readyforrelease.
1035         $this->setUser($this->teachers[0]);
1036         $data = new stdClass();
1037         $data->grade = '50.0';
1038         $data->workflowstate = ASSIGN_MARKING_WORKFLOW_STATE_READYFORRELEASE;
1039         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
1041         // Check the student can't see the grade.
1042         $this->setUser($this->students[0]);
1043         $output = $assign->view_student_summary($this->students[0], true);
1044         $this->assertEquals(false, strpos($output, '50.0'));
1046         // Mark the submission and set to released.
1047         $this->setUser($this->teachers[0]);
1048         $data = new stdClass();
1049         $data->grade = '50.0';
1050         $data->workflowstate = ASSIGN_MARKING_WORKFLOW_STATE_RELEASED;
1051         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
1053         // Check the student can see the grade.
1054         $this->setUser($this->students[0]);
1055         $output = $assign->view_student_summary($this->students[0], true);
1056         $this->assertNotEquals(false, strpos($output, '50.0'));
1057     }
1059     public function test_markerallocation() {
1060         global $PAGE;
1062         $this->setUser($this->editingteachers[0]);
1063         $assign = $this->create_instance(array('markingworkflow'=>1, 'markingallocation'=>1));
1064         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
1066         // Allocate marker to submission.
1067         $data = new stdClass();
1068         $data->allocatedmarker = $this->teachers[0]->id;
1069         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
1071         // Check the allocated marker can view the submission.
1072         $this->setUser($this->teachers[0]);
1073         $gradingtable = new assign_grading_table($assign, 100, '', 0, true);
1074         $output = $assign->get_renderer()->render($gradingtable);
1075         $this->assertEquals(true, strpos($output, $this->students[0]->lastname));
1077         // Check that other teachers can't view this submission.
1078         $this->setUser($this->teachers[1]);
1079         $gradingtable = new assign_grading_table($assign, 100, '', 0, true);
1080         $output = $assign->get_renderer()->render($gradingtable);
1081         $this->assertNotEquals(true, strpos($output, $this->students[0]->lastname));
1082     }
1084     public function test_extension_granted_event() {
1085         $this->setUser($this->editingteachers[0]);
1087         $tomorrow = time() + 24*60*60;
1088         $yesterday = time() - 24*60*60;
1090         $assign = $this->create_instance(array('duedate' => $yesterday, 'cutoffdate' => $yesterday));
1091         $sink = $this->redirectEvents();
1093         $assign->testable_save_user_extension($this->students[0]->id, $tomorrow);
1095         $events = $sink->get_events();
1096         $this->assertCount(1, $events);
1097         $event = reset($events);
1098         $this->assertInstanceOf('\mod_assign\event\extension_granted', $event);
1099         $this->assertEquals($assign->get_context(), $event->get_context());
1100         $this->assertEquals($assign->get_instance()->id, $event->objectid);
1101         $this->assertEquals($this->students[0]->id, $event->relateduserid);
1102         $expected = array(
1103             $assign->get_course()->id,
1104             'assign',
1105             'grant extension',
1106             'view.php?id=' . $assign->get_course_module()->id,
1107             $this->students[0]->id,
1108             $assign->get_course_module()->id,
1109             $this->editingteachers[0]->id
1110         );
1111         $this->assertEventLegacyLogData($expected, $event);
1112         $sink->close();
1113     }
1115     public function test_submission_locked_event() {
1116         $this->editingteachers[0]->ignoresesskey = true;
1117         $this->setUser($this->editingteachers[0]);
1119         $assign = $this->create_instance();
1120         $sink = $this->redirectEvents();
1122         $assign->lock_submission($this->students[0]->id);
1124         $events = $sink->get_events();
1125         $this->assertCount(1, $events);
1126         $event = reset($events);
1127         $this->assertInstanceOf('\mod_assign\event\submission_locked', $event);
1128         $this->assertEquals($assign->get_context(), $event->get_context());
1129         $this->assertEquals($assign->get_instance()->id, $event->objectid);
1130         $this->assertEquals($this->students[0]->id, $event->relateduserid);
1131         $expected = array(
1132             $assign->get_course()->id,
1133             'assign',
1134             'lock submission',
1135             'view.php?id=' . $assign->get_course_module()->id,
1136             get_string('locksubmissionforstudent', 'assign', array('id' => $this->students[0]->id,
1137                 'fullname' => fullname($this->students[0]))),
1138             $assign->get_course_module()->id,
1139             $this->editingteachers[0]->id
1140         );
1141         $this->assertEventLegacyLogData($expected, $event);
1142         $sink->close();
1144         // Revert to defaults.
1145         $this->editingteachers[0]->ignoresesskey = false;
1146     }
1148     public function test_identities_revealed_event() {
1149         $this->editingteachers[0]->ignoresesskey = true;
1150         $this->setUser($this->editingteachers[0]);
1152         $assign = $this->create_instance(array('blindmarking'=>1));
1153         $sink = $this->redirectEvents();
1155         $assign->reveal_identities();
1157         $events = $sink->get_events();
1158         $this->assertCount(1, $events);
1159         $event = reset($events);
1160         $this->assertInstanceOf('\mod_assign\event\identities_revealed', $event);
1161         $this->assertEquals($assign->get_context(), $event->get_context());
1162         $this->assertEquals($assign->get_instance()->id, $event->objectid);
1163         $expected = array(
1164             $assign->get_course()->id,
1165             'assign',
1166             'reveal identities',
1167             'view.php?id=' . $assign->get_course_module()->id,
1168             get_string('revealidentities', 'assign'),
1169             $assign->get_course_module()->id,
1170             $this->editingteachers[0]->id
1171         );
1172         $this->assertEventLegacyLogData($expected, $event);
1173         $sink->close();
1175         // Revert to defaults.
1176         $this->editingteachers[0]->ignoresesskey = false;
1177     }
1179     public function test_submission_status_updated_event() {
1180         $this->editingteachers[0]->ignoresesskey = true;
1181         $this->setUser($this->editingteachers[0]);
1183         $assign = $this->create_instance();
1184         $submission = $assign->get_user_submission($this->students[0]->id, true);
1185         $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
1186         $assign->testable_update_submission($submission, $this->students[0]->id, true, false);
1188         $sink = $this->redirectEvents();
1189         $assign->revert_to_draft($this->students[0]->id);
1191         $events = $sink->get_events();
1192         $this->assertCount(1, $events);
1193         $event = reset($events);
1194         $this->assertInstanceOf('\mod_assign\event\submission_status_updated', $event);
1195         $this->assertEquals($assign->get_context(), $event->get_context());
1196         $this->assertEquals($submission->id, $event->objectid);
1197         $this->assertEquals($this->students[0]->id, $event->relateduserid);
1198         $this->assertEquals(ASSIGN_SUBMISSION_STATUS_DRAFT, $event->other['newstatus']);
1199         $expected = array(
1200             $assign->get_course()->id,
1201             'assign',
1202             'revert submission to draft',
1203             'view.php?id=' . $assign->get_course_module()->id,
1204             get_string('reverttodraftforstudent', 'assign', array('id' => $this->students[0]->id,
1205                 'fullname' => fullname($this->students[0]))),
1206             $assign->get_course_module()->id,
1207             $this->editingteachers[0]->id
1208         );
1209         $this->assertEventLegacyLogData($expected, $event);
1210         $sink->close();
1212         // Revert to defaults.
1213         $this->editingteachers[0]->ignoresesskey = false;
1214     }
1216     public function test_marker_updated_event() {
1217         $this->editingteachers[0]->ignoresesskey = true;
1218         $this->setUser($this->editingteachers[0]);
1220         $assign = $this->create_instance();
1222         $sink = $this->redirectEvents();
1223         $assign->testable_process_set_batch_marking_allocation($this->students[0]->id, $this->teachers[0]->id);
1225         $events = $sink->get_events();
1226         $this->assertCount(1, $events);
1227         $event = reset($events);
1228         $this->assertInstanceOf('\mod_assign\event\marker_updated', $event);
1229         $this->assertEquals($assign->get_context(), $event->get_context());
1230         $this->assertEquals($assign->get_instance()->id, $event->objectid);
1231         $this->assertEquals($this->students[0]->id, $event->relateduserid);
1232         $this->assertEquals($this->editingteachers[0]->id, $event->userid);
1233         $this->assertEquals($this->teachers[0]->id, $event->other['markerid']);
1234         $expected = array(
1235             $assign->get_course()->id,
1236             'assign',
1237             'set marking allocation',
1238             'view.php?id=' . $assign->get_course_module()->id,
1239             get_string('setmarkerallocationforlog', 'assign', array('id' => $this->students[0]->id,
1240                 'fullname' => fullname($this->students[0]), 'marker' => fullname($this->teachers[0]))),
1241             $assign->get_course_module()->id,
1242             $this->editingteachers[0]->id
1243         );
1244         $this->assertEventLegacyLogData($expected, $event);
1245         $sink->close();
1247         // Revert to defaults.
1248         $this->editingteachers[0]->ignoresesskey = false;
1249     }
1251     public function test_workflow_state_updated_event() {
1252         $this->editingteachers[0]->ignoresesskey = true;
1253         $this->setUser($this->editingteachers[0]);
1255         $assign = $this->create_instance();
1257         $sink = $this->redirectEvents();
1258         $assign->testable_process_set_batch_marking_workflow_state($this->students[0]->id, ASSIGN_MARKING_WORKFLOW_STATE_INREVIEW);
1260         $events = $sink->get_events();
1261         $this->assertCount(1, $events);
1262         $event = reset($events);
1263         $this->assertInstanceOf('\mod_assign\event\workflow_state_updated', $event);
1264         $this->assertEquals($assign->get_context(), $event->get_context());
1265         $this->assertEquals($assign->get_instance()->id, $event->objectid);
1266         $this->assertEquals($this->students[0]->id, $event->relateduserid);
1267         $this->assertEquals($this->editingteachers[0]->id, $event->userid);
1268         $this->assertEquals(ASSIGN_MARKING_WORKFLOW_STATE_INREVIEW, $event->other['newstate']);
1269         $expected = array(
1270             $assign->get_course()->id,
1271             'assign',
1272             'set marking workflow state',
1273             'view.php?id=' . $assign->get_course_module()->id,
1274             get_string('setmarkingworkflowstateforlog', 'assign', array('id' => $this->students[0]->id,
1275                 'fullname' => fullname($this->students[0]), 'state' => ASSIGN_MARKING_WORKFLOW_STATE_INREVIEW)),
1276             $assign->get_course_module()->id,
1277             $this->editingteachers[0]->id
1278         );
1279         $this->assertEventLegacyLogData($expected, $event);
1280         $sink->close();
1282         // Revert to defaults.
1283         $this->editingteachers[0]->ignoresesskey = false;
1284     }
1286     public function test_submission_duplicated_event() {
1287         $this->setUser($this->students[0]);
1289         $assign = $this->create_instance();
1290         $submission1 = $assign->get_user_submission($this->students[0]->id, true, 0);
1291         $submission2 = $assign->get_user_submission($this->students[0]->id, true, 1);
1292         $submission2->status = ASSIGN_SUBMISSION_STATUS_REOPENED;
1293         $assign->testable_update_submission($submission2, $this->students[0]->id, time(), $assign->get_instance()->teamsubmission);
1295         $sink = $this->redirectEvents();
1296         $notices = null;
1297         $assign->copy_previous_attempt($notices);
1299         $events = $sink->get_events();
1300         $this->assertCount(1, $events);
1301         $event = reset($events);
1302         $this->assertInstanceOf('\mod_assign\event\submission_duplicated', $event);
1303         $this->assertEquals($assign->get_context(), $event->get_context());
1304         $this->assertEquals($submission2->id, $event->objectid);
1305         $this->assertEquals($this->students[0]->id, $event->userid);
1306         $submission2->status = ASSIGN_SUBMISSION_STATUS_DRAFT;
1307         $expected = array(
1308             $assign->get_course()->id,
1309             'assign',
1310             'submissioncopied',
1311             'view.php?id=' . $assign->get_course_module()->id,
1312             $assign->testable_format_submission_for_log($submission2),
1313             $assign->get_course_module()->id,
1314             $this->students[0]->id
1315         );
1316         $this->assertEventLegacyLogData($expected, $event);
1317         $sink->close();
1318     }
1320     public function test_submission_unlocked_event() {
1321         $this->editingteachers[0]->ignoresesskey = true;
1322         $this->setUser($this->editingteachers[0]);
1324         $assign = $this->create_instance();
1325         $sink = $this->redirectEvents();
1327         $assign->unlock_submission($this->students[0]->id);
1329         $events = $sink->get_events();
1330         $this->assertCount(1, $events);
1331         $event = reset($events);
1332         $this->assertInstanceOf('\mod_assign\event\submission_unlocked', $event);
1333         $this->assertEquals($assign->get_context(), $event->get_context());
1334         $this->assertEquals($assign->get_instance()->id, $event->objectid);
1335         $this->assertEquals($this->students[0]->id, $event->relateduserid);
1336         $expected = array(
1337             $assign->get_course()->id,
1338             'assign',
1339             'unlock submission',
1340             'view.php?id=' . $assign->get_course_module()->id,
1341             get_string('unlocksubmissionforstudent', 'assign', array('id' => $this->students[0]->id,
1342                 'fullname' => fullname($this->students[0]))),
1343             $assign->get_course_module()->id,
1344             $this->editingteachers[0]->id
1345         );
1346         $this->assertEventLegacyLogData($expected, $event);
1347         $sink->close();
1349         // Revert to defaults.
1350         $this->editingteachers[0]->ignoresesskey = false;
1351     }
1353     public function test_submission_graded_event() {
1354         $this->setUser($this->editingteachers[0]);
1355         $assign = $this->create_instance();
1357         // Test apply_grade_to_user.
1358         $sink = $this->redirectEvents();
1360         $data = new stdClass();
1361         $data->grade = '50.0';
1362         $assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
1363         $grade = $assign->get_user_grade($this->students[0]->id, false, 0);
1365         $events = $sink->get_events();
1366         $this->assertCount(1, $events);
1367         $event = reset($events);
1368         $this->assertInstanceOf('\mod_assign\event\submission_graded', $event);
1369         $this->assertEquals($assign->get_context(), $event->get_context());
1370         $this->assertEquals($grade->id, $event->objectid);
1371         $this->assertEquals($this->students[0]->id, $event->relateduserid);
1372         $expected = array(
1373             $assign->get_course()->id,
1374             'assign',
1375             'grade submission',
1376             'view.php?id=' . $assign->get_course_module()->id,
1377             $assign->format_grade_for_log($grade),
1378             $assign->get_course_module()->id,
1379             $this->editingteachers[0]->id
1380         );
1381         $this->assertEventLegacyLogData($expected, $event);
1382         $sink->close();
1384         // Test process_save_quick_grades.
1385         $sink = $this->redirectEvents();
1387         $data = array(
1388             'grademodified_' . $this->students[0]->id => time(),
1389             'quickgrade_' . $this->students[0]->id => '60.0'
1390         );
1391         $assign->testable_process_save_quick_grades($data);
1392         $grade = $assign->get_user_grade($this->students[0]->id, false);
1393         $this->assertEquals('60.0', $grade->grade);
1395         $events = $sink->get_events();
1396         $this->assertCount(1, $events);
1397         $event = reset($events);
1398         $this->assertInstanceOf('\mod_assign\event\submission_graded', $event);
1399         $this->assertEquals($assign->get_context(), $event->get_context());
1400         $this->assertEquals($grade->id, $event->objectid);
1401         $this->assertEquals($this->students[0]->id, $event->relateduserid);
1402         $expected = array(
1403             $assign->get_course()->id,
1404             'assign',
1405             'grade submission',
1406             'view.php?id=' . $assign->get_course_module()->id,
1407             $assign->format_grade_for_log($grade),
1408             $assign->get_course_module()->id,
1409             $this->editingteachers[0]->id
1410         );
1411         $this->assertEventLegacyLogData($expected, $event);
1412         $sink->close();
1413     }
1415     public function test_disable_submit_after_cutoff_date() {
1416         global $PAGE;
1418         $this->setUser($this->editingteachers[0]);
1419         $now = time();
1420         $tomorrow = $now + 24*60*60;
1421         $lastweek = $now - 7*24*60*60;
1422         $yesterday = $now - 24*60*60;
1424         $assign = $this->create_instance(array('duedate'=>$yesterday,
1425                                                'cutoffdate'=>$tomorrow,
1426                                                'assignsubmission_onlinetext_enabled'=>1));
1427         $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
1429         // Student should be able to see an add submission button.
1430         $this->setUser($this->students[0]);
1431         $output = $assign->view_student_summary($this->students[0], true);
1432         $this->assertNotEquals(false, strpos($output, get_string('addsubmission', 'assign')));
1434         // Add a submission but don't submit now.
1435         $submission = $assign->get_user_submission($this->students[0]->id, true);
1436         $data = new stdClass();
1437         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
1438                                          'text'=>'Submission text',
1439                                          'format'=>FORMAT_MOODLE);
1440         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
1441         $plugin->save($submission, $data);
1443         // Create another instance with cut-off and due-date already passed.
1444         $this->setUser($this->editingteachers[0]);
1445         $now = time();
1446         $assign = $this->create_instance(array('duedate'=>$lastweek,
1447                                                'cutoffdate'=>$yesterday,
1448                                                'assignsubmission_onlinetext_enabled'=>1));
1450         $this->setUser($this->students[0]);
1451         $output = $assign->view_student_summary($this->students[0], true);
1452         $this->assertNotContains($output, get_string('editsubmission', 'assign'),
1453                                  'Should not be able to edit after cutoff date.');
1454         $this->assertNotContains($output, get_string('submitassignment', 'assign'),
1455                                  'Should not be able to submit after cutoff date.');
1456     }
1457     /**
1458      * Testing for submission comment plugin settings
1459      */
1460     public function test_submission_comment_plugin_settings() {
1461         global $CFG;
1463         $commentconfig = false;
1464         if (!empty($CFG->usecomments)) {
1465             $commentconfig = $CFG->usecomments;
1466         }
1468         $CFG->usecomments = true;
1469         $assign = $this->create_instance();
1470         $plugin = $assign->get_submission_plugin_by_type('comments');
1471         $this->assertEquals(1, $plugin->is_enabled('enabled'));
1473         $assign = $this->create_instance(array('assignsubmission_comments_enabled' => 0));
1474         $plugin = $assign->get_submission_plugin_by_type('comments');
1475         $this->assertEquals(1, $plugin->is_enabled('enabled'));
1477         $assign = $this->create_instance(array('assignsubmission_comments_enabled' => 1));
1478         $plugin = $assign->get_submission_plugin_by_type('comments');
1479         $this->assertEquals(1, $plugin->is_enabled('enabled'));
1481         $CFG->usecomments = false;
1482         $assign = $this->create_instance();
1483         $plugin = $assign->get_submission_plugin_by_type('comments');
1484         $this->assertEquals(0, $plugin->is_enabled('enabled'));
1486         $assign = $this->create_instance(array('assignsubmission_comments_enabled' => 0));
1487         $plugin = $assign->get_submission_plugin_by_type('comments');
1488         $this->assertEquals(0, $plugin->is_enabled('enabled'));
1490         $assign = $this->create_instance(array('assignsubmission_comments_enabled' => 1));
1491         $plugin = $assign->get_submission_plugin_by_type('comments');
1492         $this->assertEquals(0, $plugin->is_enabled('enabled'));
1494         $CFG->usecomments = $commentconfig;
1495     }
1497     /**
1498      * Testing for comment inline settings
1499      */
1500     public function test_feedback_comment_commentinline() {
1501         global $CFG;
1503         $sourcetext = "Hello!
1505 I'm writing to you from the Moodle Majlis in Muscat, Oman, where we just had several days of Moodle community goodness.
1507 URL outside a tag: https://moodle.org/logo/logo-240x60.gif
1508 Plugin url outside a tag: @@PLUGINFILE@@/logo-240x60.gif
1510 External link 1:<img src='https://moodle.org/logo/logo-240x60.gif' alt='Moodle'/>
1511 External link 2:<img alt=\"Moodle\" src=\"https://moodle.org/logo/logo-240x60.gif\"/>
1512 Internal link 1:<img src='@@PLUGINFILE@@/logo-240x60.gif' alt='Moodle'/>
1513 Internal link 2:<img alt=\"Moodle\" src=\"@@PLUGINFILE@@logo-240x60.gif\"/>
1514 Anchor link 1:<a href=\"@@PLUGINFILE@@logo-240x60.gif\" alt=\"bananas\">Link text</a>
1515 Anchor link 2:<a title=\"bananas\" href=\"../logo-240x60.gif\">Link text</a>
1516 ";
1518         // Note the internal images have been stripped and the html is purified (quotes fixed in this case).
1519         $filteredtext = "Hello!
1521 I'm writing to you from the Moodle Majlis in Muscat, Oman, where we just had several days of Moodle community goodness.
1523 URL outside a tag: https://moodle.org/logo/logo-240x60.gif
1524 Plugin url outside a tag: @@PLUGINFILE@@/logo-240x60.gif
1526 External link 1:<img src=\"https://moodle.org/logo/logo-240x60.gif\" alt=\"Moodle\" />
1527 External link 2:<img alt=\"Moodle\" src=\"https://moodle.org/logo/logo-240x60.gif\" />
1528 Internal link 1:
1529 Internal link 2:
1530 Anchor link 1:Link text
1531 Anchor link 2:<a title=\"bananas\" href=\"../logo-240x60.gif\">Link text</a>
1532 ";
1534         $this->setUser($this->editingteachers[0]);
1535         $params = array('assignsubmission_onlinetext_enabled' => 1,
1536                         'assignfeedback_comments_enabled' => 1,
1537                         'assignfeedback_comments_commentinline' => 1);
1538         $assign = $this->create_instance($params);
1540         $this->setUser($this->students[0]);
1541         // Add a submission but don't submit now.
1542         $submission = $assign->get_user_submission($this->students[0]->id, true);
1543         $data = new stdClass();
1545         // Test the internal link is stripped, but the external one is not.
1546         $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
1547                                          'text'=>$sourcetext,
1548                                          'format'=>FORMAT_MOODLE);
1550         $plugin = $assign->get_submission_plugin_by_type('onlinetext');
1551         $plugin->save($submission, $data);
1553         $this->setUser($this->editingteachers[0]);
1555         $data = new stdClass();
1556         require_once($CFG->dirroot . '/mod/assign/gradeform.php');
1557         $pagination = array('userid'=>$this->students[0]->id,
1558                             'rownum'=>0,
1559                             'last'=>true,
1560                             'useridlistid'=>time(),
1561                             'attemptnumber'=>0);
1562         $formparams = array($assign, $data, $pagination);
1563         $mform = new mod_assign_grade_form(null, $formparams);
1565         $this->assertEquals($filteredtext, $data->assignfeedbackcomments_editor['text']);
1566     }
1568     /**
1569      * Testing for feedback comment plugin settings
1570      */
1571     public function test_feedback_plugin_settings() {
1573         $assign = $this->create_instance();
1574         $plugin = $assign->get_feedback_plugin_by_type('comments');
1575         $this->assertEquals(0, $plugin->is_enabled('enabled'));
1577         $assign = $this->create_instance(array('assignfeedback_comments_enabled' => 0));
1578         $plugin = $assign->get_feedback_plugin_by_type('comments');
1579         $this->assertEquals(0, $plugin->is_enabled('enabled'));
1581         $assign = $this->create_instance(array('assignfeedback_comments_enabled' => 1));
1582         $plugin = $assign->get_feedback_plugin_by_type('comments');
1583         $this->assertEquals(1, $plugin->is_enabled('enabled'));
1584     }