$nextpageparams['action'] = 'grading';
} else if ($action == 'confirmsubmit') {
$action = 'submit';
- if ($this->process_submit_for_grading($mform)) {
+ if ($this->process_submit_for_grading($mform, $notices)) {
$action = 'redirect';
$nextpageparams['action'] = 'view';
+ } else if ($notices) {
+ $action = 'viewsubmitforgradingerror';
+ }
+ } else if ($action == 'submitotherforgrading') {
+ if ($this->process_submit_other_for_grading($mform, $notices)) {
+ $action = 'redirect';
+ $nextpageparams['action'] = 'grading';
+ } else {
+ $action = 'viewsubmitforgradingerror';
}
} else if ($action == 'gradingbatchoperation') {
$action = $this->process_grading_batch_operation($mform);
$o .= $this->view_batch_set_workflow_state($mform);
} else if ($action == 'viewbatchmarkingallocation') {
$o .= $this->view_batch_markingallocation($mform);
+ } else if ($action == 'viewsubmitforgradingerror') {
+ $o .= $this->view_error_page(get_string('submitforgrading', 'assign'), $notices);
} else {
$o .= $this->view_submission_page();
}
/**
* Message for students when assignment submissions have been closed.
*
+ * @param string $title The page title
+ * @param array $notices The array of notices to show.
* @return string
*/
- protected function view_student_error_message() {
+ protected function view_notices($title, $notices) {
global $CFG;
$o = '';
- // Need submit permission to submit an assignment.
- require_capability('mod/assign:submit', $this->context);
$header = new assign_header($this->get_instance(),
$this->get_context(),
$this->show_intro(),
$this->get_course_module()->id,
- get_string('editsubmission', 'assign'));
+ $title);
$o .= $this->get_renderer()->render($header);
- $o .= $this->get_renderer()->notification(get_string('submissionsclosed', 'assign'));
+ foreach ($notices as $notice) {
+ $o .= $this->get_renderer()->notification($notice);
+ }
+
+ $url = new moodle_url('/mod/assign/view.php', array('id'=>$this->get_course_module()->id, 'action'=>'view'));
+ $o .= $this->get_renderer()->continue_button($url);
$o .= $this->view_footer();
return $o;
+ }
+ /**
+ * Get the name for a user - hiding their real name if blind marking is on.
+ *
+ * @param stdClass $user The user record as required by fullname()
+ * @return string The name.
+ */
+ protected function fullname($user) {
+ if ($this->is_blind_marking()) {
+ $uniqueid = $this->get_uniqueid_for_user($userid);
+ return get_string('participant', 'assign') . ' ' . $uniqueid;
+ } else {
+ return fullname($user);
+ }
}
/**
* @return string The page output.
*/
protected function view_edit_submission_page($mform, $notices) {
- global $CFG;
+ global $CFG, $USER, $DB;
$o = '';
require_once($CFG->dirroot . '/mod/assign/submission_form.php');
// Need submit permission to submit an assignment.
- require_capability('mod/assign:submit', $this->context);
+ $userid = optional_param('userid', $USER->id, PARAM_INT);
+ $user = clone($USER);
+ if ($userid == $USER->id) {
+ // User is editing their own submission.
+ require_capability('mod/assign:submit', $this->context);
+ $title = get_string('editsubmission', 'assign');
+ } else {
+ // User is editing another user's submission.
+ if (!$this->can_edit_submission($userid, $USER->id)) {
+ print_error('nopermission');
+ }
- if (!$this->submissions_open()) {
- return $this->view_student_error_message();
+ $user = $DB->get_record('user', array('id'=>$userid), '*', MUST_EXIST);
+ $name = $this->fullname($user);
+ $title = get_string('editsubmissionother', 'assign', $name);
}
+
+ if (!$this->submissions_open($userid)) {
+ $message = array(get_string('submissionsclosed', 'assign'));
+ return $this->view_notices($title, $message);
+ }
+
$o .= $this->get_renderer()->render(new assign_header($this->get_instance(),
$this->get_context(),
$this->show_intro(),
$this->get_course_module()->id,
- get_string('editsubmission', 'assign')));
- $o .= $this->plagiarism_print_disclosure();
+ $title));
+ if ($userid == $USER->id) {
+ // We only show this if it their submission.
+ $o .= $this->plagiarism_print_disclosure();
+ }
$data = new stdClass();
-
+ $data->userid = $userid;
if (!$mform) {
$mform = new mod_assign_submission_form(null, array($this, $data));
}
$o .= $this->get_renderer()->render(new assign_form('editsubmissionform', $mform));
$o .= $this->view_footer();
- $this->add_to_log('view submit assignment form', get_string('viewownsubmissionform', 'assign'));
+ $this->add_to_log('view submit assignment form', $title);
return $o;
}
}
+ /**
+ * Capability check to make sure this grader can edit this submission.
+ *
+ * @param int $userid - The user whose submission is to be edited
+ * @param int $graderid (optional) - The user who will do the editing (default to $USER->id).
+ * @return bool
+ */
+ public function can_edit_submission($userid, $graderid = 0) {
+ global $USER;
+
+ if (empty($graderid)) {
+ $graderid = $USER->id;
+ }
+
+ if ($userid == $graderid &&
+ $this->submissions_open($userid) &&
+ has_capability('mod/assign:submit', $this->context, $graderid)) {
+ // User can edit their own submission.
+ return true;
+ }
+
+ if (!has_capability('mod/assign:editothersubmission', $this->context, $graderid)) {
+ return false;
+ }
+
+ $cm = $this->get_course_module();
+ if (groups_get_activity_groupmode($cm) == SEPARATEGROUPS) {
+ // These arrays are indexed by groupid.
+ $studentgroups = array_keys(groups_get_activity_allowed_groups($cm, $userid));
+ $gradergroups = array_keys(groups_get_activity_allowed_groups($cm, $graderid));
+
+ return count(array_intersect($studentgroups, $gradergroups)) > 0;
+ }
+ return true;
+ }
+
/**
* Returns a list of teachers that should be grading given submission.
*
} else {
$user = $USER;
}
- $this->send_notification($user,
- $user,
- 'submissionreceipt',
- 'assign_notification',
- $submission->timemodified);
+ if ($submission->userid == $USER->id) {
+ $this->send_notification(core_user::get_noreply_user(),
+ $user,
+ 'submissionreceipt',
+ 'assign_notification',
+ $submission->timemodified);
+ } else {
+ $this->send_notification($USER,
+ $user,
+ 'submissionreceiptother',
+ 'assign_notification',
+ $submission->timemodified);
+ }
}
/**
/**
* Submit a submission for grading.
*
+ * @param stdClass $data - The form data
+ * @param array $notices - List of error messages to display on an error condition.
* @return bool Return false if the submission was not submitted.
*/
- public function submit_for_grading($data) {
+ public function submit_for_grading($data, $notices) {
global $USER;
+ $userid = $USER->id;
+ if (!empty($data->userid)) {
+ $userid = $data->userid;
+ }
// Need submit permission to submit an assignment.
- require_capability('mod/assign:submit', $this->context);
+ if ($userid == $USER->id) {
+ require_capability('mod/assign:submit', $this->context);
+ } else {
+ if (!$this->can_edit_submission($userid, $USER->id)) {
+ print_error('nopermission');
+ }
+ }
$instance = $this->get_instance();
if ($instance->teamsubmission) {
- $submission = $this->get_group_submission($USER->id, 0, true);
+ $submission = $this->get_group_submission($userid, 0, true);
} else {
- $submission = $this->get_user_submission($USER->id, true);
+ $submission = $this->get_user_submission($userid, true);
}
- if (!$this->submissions_open($USER->id)) {
+ if (!$this->submissions_open($userid)) {
+ $notices[] = get_string('submissionsclosed', 'assign');
return false;
}
- if ($instance->requiresubmissionstatement && !$data->submissionstatement) {
+ if ($instance->requiresubmissionstatement && empty($data->submissionstatement) && $USER->id == $userid) {
return false;
}
}
$submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
- $this->update_submission($submission, $USER->id, true, $instance->teamsubmission);
+ $this->update_submission($submission, $userid, true, $instance->teamsubmission);
$completion = new completion_info($this->get_course());
if ($completion->is_enabled($this->get_course_module()) && $instance->completionsubmit) {
- $completion->update_state($this->get_course_module(), COMPLETION_COMPLETE, $USER->id);
+ $completion->update_state($this->get_course_module(), COMPLETION_COMPLETE, $userid);
}
- if (!empty($data->submissionstatement)) {
+ if (!empty($data->submissionstatement) && $USER->id == $userid) {
$logmessage = get_string('submissionstatementacceptedlog',
'mod_assign',
fullname($USER));
$event->trigger();
return true;
}
+ $notices[] = get_string('submissionsclosed', 'assign');
return false;
}
+ /**
+ * A students submission is submitted for grading by a teacher.
+ *
+ * @return bool
+ */
+ protected function process_submit_other_for_grading($mform, $notices) {
+ global $USER, $CFG;
+
+ require_sesskey();
+
+ $userid = optional_param('userid', $USER->id, PARAM_INT);
+
+ if (!$this->submissions_open($userid)) {
+ $notices[] = get_string('submissionsclosed', 'assign');
+ return false;
+ }
+ $data = new stdClass();
+ $data->userid = $userid;
+ return $this->submit_for_grading($data, $notices);
+ }
+
/**
* Assignment submission is processed before grading.
*
* It can be null.
* @return bool Return false if the validation fails. This affects which page is displayed next.
*/
- protected function process_submit_for_grading($mform) {
- global $USER, $CFG;
+ protected function process_submit_for_grading($mform, $notices) {
+ global $CFG;
require_once($CFG->dirroot . '/mod/assign/submissionconfirmform.php');
require_sesskey();
if (!$this->submissions_open()) {
- return $this->view_student_error_message();
+ $notices[] = get_string('submissionsclosed', 'assign');
+ return false;
}
$instance = $this->get_instance();
$data = new stdClass();
if ($mform->get_data() == false) {
return false;
}
- return $this->submit_for_grading($data);
+ return $this->submit_for_grading($data, $notices);
}
return true;
}
* @return string
*/
protected function format_submission_for_log(stdClass $submission) {
+ global $DB;
+
$info = '';
- $info .= get_string('submissionstatus', 'assign') .
- ': ' .
- get_string('submissionstatus_' . $submission->status, 'assign') .
- '. <br>';
+ if ($submission->userid) {
+ $user = $DB->get_record('user', array('id' => $submission->userid), '*', MUST_EXIST);
+ $name = fullname($user);
+ } else {
+ $group = $DB->get_record('groups', array('id' => $submission->groupid), '*', MUST_EXIST);
+ $name = $group->name;
+ }
+ $status = get_string('submissionstatus_' . $submission->status, 'assign');
+ $params = array('id'=>$submission->userid, 'fullname'=>$name, 'status'=>$status);
+ $info .= get_string('submissionlog', 'assign', $params) . ' <br>';
foreach ($this->submissionplugins as $plugin) {
if ($plugin->is_enabled() && $plugin->is_visible()) {
* @return bool
*/
public function save_submission(stdClass $data, & $notices) {
- global $CFG, $USER;
+ global $CFG, $USER, $DB;
- require_capability('mod/assign:submit', $this->context);
+ $userid = $USER->id;
+ if (!empty($data->userid)) {
+ $userid = $data->userid;
+ }
+
+ $user = clone($USER);
+ if ($userid == $USER->id) {
+ require_capability('mod/assign:submit', $this->context);
+ } else {
+ $user = $DB->get_record('user', array('id'=>$userid), '*', MUST_EXIST);
+ if (!$this->can_edit_submission($userid, $USER->id)) {
+ print_error('nopermission');
+ }
+ }
$instance = $this->get_instance();
if ($instance->teamsubmission) {
- $submission = $this->get_group_submission($USER->id, 0, true);
+ $submission = $this->get_group_submission($userid, 0, true);
} else {
- $submission = $this->get_user_submission($USER->id, true);
+ $submission = $this->get_user_submission($userid, true);
}
if ($instance->submissiondrafts) {
$submission->status = ASSIGN_SUBMISSION_STATUS_DRAFT;
$submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
}
- $flags = $this->get_user_flags($USER->id, false);
+ $flags = $this->get_user_flags($userid, false);
// Get the flags to check if it is locked.
if ($flags && $flags->locked) {
return false;
}
- $this->update_submission($submission, $USER->id, true, $instance->teamsubmission);
+ $this->update_submission($submission, $userid, true, $instance->teamsubmission);
// Logging.
- if (isset($data->submissionstatement)) {
+ if (isset($data->submissionstatement) && ($userid == $USER->id)) {
$logmessage = get_string('submissionstatementacceptedlog',
'mod_assign',
fullname($USER));
}
$completion = new completion_info($this->get_course());
if ($completion->is_enabled($this->get_course_module()) && $instance->completionsubmit) {
- $completion->update_state($this->get_course_module(), $complete, $USER->id);
+ $completion->update_state($this->get_course_module(), $complete, $userid);
}
if (!$instance->submissiondrafts) {
* @return bool
*/
protected function process_save_submission(&$mform, &$notices) {
- global $CFG;
+ global $CFG, $USER;
// Include submission form.
require_once($CFG->dirroot . '/mod/assign/submission_form.php');
+ $userid = optional_param('userid', $USER->id, PARAM_INT);
// Need submit permission to submit an assignment.
require_sesskey();
- if (!$this->submissions_open()) {
+ if (!$this->submissions_open($userid)) {
$notices[] = get_string('duedatereached', 'assign');
return false;
}
$instance = $this->get_instance();
$data = new stdClass();
+ $data->userid = $userid;
$mform = new mod_assign_submission_form(null, array($this, $data));
if ($mform->is_cancelled()) {
return true;
public function add_submission_form_elements(MoodleQuickForm $mform, stdClass $data) {
global $USER;
+ $userid = $data->userid;
// Team submissions.
if ($this->get_instance()->teamsubmission) {
- $submission = $this->get_group_submission($USER->id, 0, false);
+ $submission = $this->get_group_submission($userid, 0, false);
} else {
- $submission = $this->get_user_submission($USER->id, false);
+ $submission = $this->get_user_submission($userid, false);
}
// Submission statement.
$draftsenabled = $this->get_instance()->submissiondrafts;
- if ($requiresubmissionstatement && !$draftsenabled) {
+ // Only show submission statement if we are editing our own submission.
+ if ($requiresubmissionstatement && !$draftsenabled && $userid == $USER->id) {
$submissionstatement = '';
if (!empty($adminconfig->submissionstatement)) {
$mform->addRule('submissionstatement', get_string('required'), 'required', null, 'client');
}
- $this->add_plugin_submission_elements($submission, $mform, $data, $USER->id);
+ $this->add_plugin_submission_elements($submission, $mform, $data, $userid);
// Hidden params.
$mform->addElement('hidden', 'id', $this->get_course_module()->id);
$mform->setType('id', PARAM_INT);
+ $mform->addElement('hidden', 'userid', $userid);
+ $mform->setType('userid', PARAM_INT);
+
$mform->addElement('hidden', 'action', 'savesubmission');
$mform->setType('action', PARAM_TEXT);
}
$this->editingteachers[0]->ignoresesskey = false;
}
+ public function test_teacher_submit_for_student() {
+ global $PAGE;
+
+ $this->preventResetByRollback();
+ $sink = $this->redirectMessages();
+
+ $this->setUser($this->editingteachers[0]);
+
+ $assign = $this->create_instance(array('assignsubmission_onlinetext_enabled'=>1, 'submissiondrafts'=>1));
+ $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
+
+ $this->setUser($this->students[0]);
+ // Simulate a submission.
+ $data = new stdClass();
+ $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
+ 'text'=>'Student submission text',
+ 'format'=>FORMAT_MOODLE);
+
+ $notices = array();
+ $assign->save_submission($data, $notices);
+
+ // Check that the submission text was saved.
+ $output = $assign->view_student_summary($this->students[0], true);
+ $this->assertContains('Student submission text', $output, 'Contains student submission text');
+
+ // Check that a teacher teacher with the extra capability can edit a students submission.
+ $this->setUser($this->teachers[0]);
+ $data = new stdClass();
+ $data->userid = $this->students[0]->id;
+ $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
+ 'text'=>'Teacher edited submission text',
+ 'format'=>FORMAT_MOODLE);
+
+ // Add the required capability.
+ $roleid = create_role('Dummy role', 'dummyrole', 'dummy role description');
+ assign_capability('mod/assign:editothersubmission', CAP_ALLOW, $roleid, $assign->get_context()->id);
+ role_assign($roleid, $this->teachers[0]->id, $assign->get_context()->id);
+ accesslib_clear_all_caches_for_unit_testing();
+
+ // Try to save the submission.
+ $notices = array();
+ $assign->save_submission($data, $notices);
+
+ // Check that the teacher can submit the students work.
+ $data = new stdClass();
+ $data->userid = $this->students[0]->id;
+ $notices = array();
+ $assign->submit_for_grading($data, $notices);
+
+ // Revert to draft so the student can edit it.
+ $assign->revert_to_draft($this->students[0]->id);
+
+ $this->setUser($this->students[0]);
+
+ // Check that the submission text was saved.
+ $output = $assign->view_student_summary($this->students[0], true);
+ $this->assertContains('Teacher edited submission text', $output, 'Contains student submission text');
+
+ // Check that the student can submit their work.
+ $data = new stdClass();
+ $assign->submit_for_grading($data, $notices);
+
+ $output = $assign->view_student_summary($this->students[0], true);
+ $this->assertNotContains(get_string('addsubmission', 'assign'), $output);
+
+ // Set to a default editing teacher who should not be able to edit this submission.
+ $this->setUser($this->editingteachers[1]);
+
+ // Revert to draft so the submission is editable.
+ $assign->revert_to_draft($this->students[0]->id);
+
+ $data = new stdClass();
+ $data->userid = $this->students[0]->id;
+ $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
+ 'text'=>'Teacher 2 edited submission text',
+ 'format'=>FORMAT_MOODLE);
+
+ $notices = array();
+ $this->setExpectedException('moodle_exception');
+ $assign->save_submission($data, $notices);
+
+ $sink->close();
+ }
+
public function test_marker_updated_event() {
$this->editingteachers[0]->ignoresesskey = true;
$this->setUser($this->editingteachers[0]);
$plugin = $assign->get_feedback_plugin_by_type($gradebookplugintype);
$this->assertEquals(0, $plugin->is_enabled('enabled'));
}
+
+ /**
+ * Testing can_edit_submission
+ */
+ public function test_can_edit_submission() {
+ global $PAGE, $DB;
+ $this->create_extra_users();
+
+ $this->setAdminUser();
+ // Create assignment (onlinetext).
+ $assign = $this->create_instance(array('assignsubmission_onlinetext_enabled'=>1, 'submissiondrafts'=>1));
+ $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
+
+ // Check student can edit their own submission.
+ $this->assertTrue($assign->can_edit_submission($this->students[0]->id, $this->students[0]->id));
+ // Check student cannot edit others submission.
+ $this->assertFalse($assign->can_edit_submission($this->students[0]->id, $this->students[1]->id));
+
+ // Check teacher cannot (by default) edit a students submission.
+ $this->assertFalse($assign->can_edit_submission($this->students[0]->id, $this->teachers[0]->id));
+
+ // Add the required capability to edit a student submission.
+ $roleid = create_role('Dummy role', 'dummyrole', 'dummy role description');
+ assign_capability('mod/assign:editothersubmission', CAP_ALLOW, $roleid, $assign->get_context()->id);
+ role_assign($roleid, $this->teachers[0]->id, $assign->get_context()->id);
+ accesslib_clear_all_caches_for_unit_testing();
+ // Retest - should now have access.
+ $this->assertTrue($assign->can_edit_submission($this->students[0]->id, $this->teachers[0]->id));
+
+ // Force create an assignment with SEPARATEGROUPS.
+ $data = new stdClass();
+ $data->courseid = $this->course->id;
+ $data->name = 'Grouping';
+ $groupingid = groups_create_grouping($data);
+ groups_assign_grouping($groupingid, $this->groups[0]->id);
+ groups_assign_grouping($groupingid, $this->groups[1]->id);
+ $assign = $this->create_instance(array('groupingid' => $groupingid, 'groupmode' => SEPARATEGROUPS));
+
+ // Add the capability to the new assignment for extra students 0 and 1.
+ assign_capability('mod/assign:editothersubmission', CAP_ALLOW, $roleid, $assign->get_context()->id);
+ role_assign($roleid, $this->extrastudents[0]->id, $assign->get_context()->id);
+ role_assign($roleid, $this->extrastudents[1]->id, $assign->get_context()->id);
+ accesslib_clear_all_caches_for_unit_testing();
+
+ // Verify the extra student does not have the capability to edit a submission not in their group.
+ $this->assertFalse($assign->can_edit_submission($this->students[0]->id, $this->extrastudents[1]->id));
+ // Verify the extra student does have the capability to edit a submission in their group.
+ $this->assertTrue($assign->can_edit_submission($this->students[0]->id, $this->extrastudents[0]->id));
+
+ }
}