diff --git a/mod/assign/db/access.php b/mod/assign/db/access.php index e68af3a811e..89815ab52f0 100644 --- a/mod/assign/db/access.php +++ b/mod/assign/db/access.php @@ -82,6 +82,12 @@ $capabilities = array( 'clonepermissionsfrom' => 'moodle/course:manageactivities' ), + 'mod/assign:editothersubmission' => array( + 'riskbitmask' => RISK_MANAGETRUST|RISK_DATALOSS|RISK_PERSONAL, + 'captype' => 'write', + 'contextlevel' => CONTEXT_MODULE + ), + 'mod/assign:grantextension' => array( 'captype' => 'write', 'contextlevel' => CONTEXT_MODULE, diff --git a/mod/assign/externallib.php b/mod/assign/externallib.php index fd28223c4cc..dc6e06ab646 100644 --- a/mod/assign/externallib.php +++ b/mod/assign/externallib.php @@ -1422,9 +1422,10 @@ class mod_assign_external extends external_api { $warnings = array(); $data = new stdClass(); $data->submissionstatement = $acceptsubmissionstatement; + $notices = array(); - if (!$assignment->submit_for_grading($data)) { - $detail = 'User id: ' . $USER->id . ', Assignment id: ' . $assignmentid; + if (!$assignment->submit_for_grading($data, $notices)) { + $detail = 'User id: ' . $USER->id . ', Assignment id: ' . $assignmentid . ' Notices:' . implode(', ', $notices); $warnings[] = self::generate_warning($assignmentid, 'couldnotsubmitforgrading', $detail); diff --git a/mod/assign/gradingtable.php b/mod/assign/gradingtable.php index 5c96c3cb190..fe5f1e66ce8 100644 --- a/mod/assign/gradingtable.php +++ b/mod/assign/gradingtable.php @@ -962,6 +962,8 @@ class assign_grading_table extends table_sql implements renderable { * @return string */ public function col_userid(stdClass $row) { + global $USER; + $edit = ''; $actions = array(); @@ -978,6 +980,9 @@ class assign_grading_table extends table_sql implements renderable { } $actions[$url->out(false)] = $description; + $submissionsopen = $this->assignment->submissions_open($row->id); + $caneditsubmission = $this->assignment->can_edit_submission($row->id, $USER->id); + // Hide for offline assignments. if ($this->assignment->is_any_submission_plugin_enabled()) { if (!$row->status || @@ -1018,6 +1023,18 @@ class assign_grading_table extends table_sql implements renderable { $description = get_string('grantextension', 'assign'); $actions[$url->out(false)] = $description; } + if ($submissionsopen && + $USER->id != $row->id && + $caneditsubmission) { + $urlparams = array('id' => $this->assignment->get_course_module()->id, + 'userid'=>$row->id, + 'action'=>'editsubmission', + 'sesskey'=>sesskey(), + 'page'=>$this->currpage); + $url = new moodle_url('/mod/assign/view.php', $urlparams); + $description = get_string('editsubmission', 'assign'); + $actions[$url->out(false)] = $description; + } } if ($row->status == ASSIGN_SUBMISSION_STATUS_SUBMITTED && $this->assignment->get_instance()->submissiondrafts) { @@ -1030,6 +1047,20 @@ class assign_grading_table extends table_sql implements renderable { $description = get_string('reverttodraftshort', 'assign'); $actions[$url->out(false)] = $description; } + if ($row->status == ASSIGN_SUBMISSION_STATUS_DRAFT && + $this->assignment->get_instance()->submissiondrafts && + $caneditsubmission && + $submissionsopen && + $row->id != $USER->id) { + $urlparams = array('id' => $this->assignment->get_course_module()->id, + 'userid'=>$row->id, + 'action'=>'submitotherforgrading', + 'sesskey'=>sesskey(), + 'page'=>$this->currpage); + $url = new moodle_url('/mod/assign/view.php', $urlparams); + $description = get_string('submitforgrading', 'assign'); + $actions[$url->out(false)] = $description; + } $ismanual = $this->assignment->get_instance()->attemptreopenmethod == ASSIGN_ATTEMPT_REOPEN_METHOD_MANUAL; $hassubmission = !empty($row->status); diff --git a/mod/assign/lang/en/assign.php b/mod/assign/lang/en/assign.php index b677b200bf1..1f09a49401f 100644 --- a/mod/assign/lang/en/assign.php +++ b/mod/assign/lang/en/assign.php @@ -42,6 +42,7 @@ $string['alwaysshowdescription_help'] = 'If disabled, the Assignment Description $string['applytoteam'] = 'Apply grades and feedback to entire group'; $string['assign:addinstance'] = 'Add a new assignment'; $string['assign:exportownsubmission'] = 'Export own submission'; +$string['assign:editothersubmission'] = 'Edit another students submission'; $string['assign:grade'] = 'Grade assignment'; $string['assign:grantextension'] = 'Grant extension'; $string['assign:manageallocations'] = 'Manage markers allocated to submissions'; @@ -135,6 +136,7 @@ $string['duedatevalidation'] = 'Due date must be after the allow submissions fro $string['editattemptfeedback'] = 'Edit the grade and feedback for attempt number {$a}.'; $string['editingpreviousfeedbackwarning'] = 'You are editing the feedback for a previous attempt. This is attempt {$a->attemptnumber} out of {$a->totalattempts}.'; $string['editsubmission'] = 'Edit submission'; +$string['editsubmissionother'] = 'Edit submission for {$a}'; $string['editsubmission_help'] = 'Make changes to your submission'; $string['editingstatus'] = 'Editing status'; $string['editaction'] = 'Actions...'; @@ -350,6 +352,16 @@ $string['submissionnoteditable'] = 'Student cannot edit this submission'; $string['submissionnotready'] = 'This assignment is not ready to submit:'; $string['submissionplugins'] = 'Submission plugins'; $string['submissionreceipts'] = 'Send submission receipts'; +$string['submissionreceiptothertext'] = 'Your assignment submission for +\'{$a->assignment}\' has been submitted. + +You can see the status of your assignment submission: + + {$a->url}'; +$string['submissionreceiptotherhtml'] = 'Your assignment submission for +\'{$a->assignment}\' has been submitted.

+You can see the status of your assignment submission.'; +$string['submissionreceiptothersmall'] = 'Your assignment submission for {$a->assignment} has been submitted.'; $string['submissionreceipttext'] = 'You have submitted an assignment submission for \'{$a->assignment}\' @@ -381,6 +393,7 @@ $string['submissionsummary'] = '{$a->status}. Last modified on {$a->timemodified $string['submissionteam'] = 'Group'; $string['submission'] = 'Submission'; $string['submitaction'] = 'Submit'; +$string['submitforgrading'] = 'Submit for grading'; $string['submitassignment_help'] = 'Once this assignment is submitted you will not be able to make any more changes.'; $string['submitassignment'] = 'Submit assignment'; $string['submittedearly'] = 'Assignment was submitted {$a} early'; @@ -426,3 +439,4 @@ $string['workflowfilter'] = 'Workflow filter'; $string['submissiontypes'] = 'Submission types'; $string['feedbacktypes'] = 'Feedback types'; $string['groupsubmissionsettings'] = 'Group submission settings'; +$string['submissionlog'] = 'Student: {$a->fullname}, Status: {$a->status}'; diff --git a/mod/assign/locallib.php b/mod/assign/locallib.php index 404b6956ea3..fc62b564e74 100644 --- a/mod/assign/locallib.php +++ b/mod/assign/locallib.php @@ -417,9 +417,18 @@ class assign { $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); @@ -521,6 +530,8 @@ class assign { $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(); } @@ -3187,28 +3198,47 @@ class assign { /** * 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); + } } /** @@ -3220,24 +3250,44 @@ class assign { * @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)); } @@ -3249,7 +3299,7 @@ class assign { $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; } @@ -4265,6 +4315,42 @@ class assign { } + /** + * 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. * @@ -4536,11 +4622,19 @@ class assign { } 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); + } } /** @@ -4580,27 +4674,40 @@ class assign { /** * 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; } @@ -4614,13 +4721,13 @@ class assign { } $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)); @@ -4643,9 +4750,31 @@ class assign { $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. * @@ -4653,14 +4782,15 @@ class assign { * 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(); @@ -4685,7 +4815,7 @@ class assign { if ($mform->get_data() == false) { return false; } - return $this->submit_for_grading($data); + return $this->submit_for_grading($data, $notices); } return true; } @@ -5161,11 +5291,19 @@ class assign { * @return string */ protected function format_submission_for_log(stdClass $submission) { + global $DB; + $info = ''; - $info .= get_string('submissionstatus', 'assign') . - ': ' . - get_string('submissionstatus_' . $submission->status, 'assign') . - '.
'; + 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) . '
'; foreach ($this->submissionplugins as $plugin) { if ($plugin->is_enabled() && $plugin->is_visible()) { @@ -5319,15 +5457,28 @@ class assign { * @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; @@ -5335,7 +5486,7 @@ class assign { $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) { @@ -5360,10 +5511,10 @@ class assign { 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)); @@ -5392,7 +5543,7 @@ class assign { } $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) { @@ -5421,20 +5572,22 @@ class assign { * @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; @@ -5839,11 +5992,12 @@ class assign { 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. @@ -5854,7 +6008,8 @@ class assign { $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)) { @@ -5864,12 +6019,15 @@ class assign { $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); } diff --git a/mod/assign/tests/locallib_test.php b/mod/assign/tests/locallib_test.php index a1ca542b740..758f29506b9 100644 --- a/mod/assign/tests/locallib_test.php +++ b/mod/assign/tests/locallib_test.php @@ -1278,6 +1278,90 @@ class mod_assign_locallib_testcase extends mod_assign_base_testcase { $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]); @@ -1669,5 +1753,55 @@ Anchor link 2:Link text $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)); + + } } diff --git a/mod/assign/version.php b/mod/assign/version.php index a2e1f9a730a..46acd8a567a 100644 --- a/mod/assign/version.php +++ b/mod/assign/version.php @@ -25,7 +25,7 @@ defined('MOODLE_INTERNAL') || die(); $module->component = 'mod_assign'; // Full name of the plugin (used for diagnostics). -$module->version = 2014010801; // The current module version (Date: YYYYMMDDXX). +$module->version = 2014011500; // The current module version (Date: YYYYMMDDXX). $module->requires = 2013110500; // Requires this Moodle version. $module->cron = 60;