/** * To make up for the fact that in 2.0 there is no screen with both quiz question and feedback * text-entry box next to each other (the feedback bit is a separate pop-up), we have to make * a custom form to allow grading to happen. It is based on code from * /mod/quiz/reviewquestion.php * * Use questionid, rather than slot so we can group the same question in future, even across * random questions. * * @param array $params all of the stuff sent with the node click e.g. questionid * @param object $coursemodule * @throws moodle_exception * @global stdClass $CFG * @global moodle_database $DB * @return string the HTML page */ public function grading_popup($params, $coursemodule) { global $CFG, $PAGE, $DB, $OUTPUT; $output = ''; // TODO what params do we get here? require_once $CFG->dirroot . '/mod/quiz/locallib.php'; // TODO feed in all dynamic variables here. $PAGE->set_url(new moodle_url('/blocks/ajax_marking/actions/grading_popup.php', $params)); $formattributes = array('method' => 'post', 'class' => 'mform', 'id' => 'manualgradingform', 'action' => block_ajax_marking_form_url($params)); $output .= html_writer::start_tag('form', $formattributes); // We could be looking at multiple attempts and/or multiple questions // Assume we have a user/quiz combo to get us here. We may have attemptid or questionid too. // Get all attempts with unmarked questions. We may or may not have a questionid, but // this comes later so we can use the quiz's internal functions. $questionattempts = $this->get_question_attempts($params); if (!$questionattempts) { $message = 'Could not retrieve question attempts. Maybe someone else marked them just now'; throw new moodle_exception($message); } // Print infobox. $rows = array(); // Print user picture and name. $quizattempts = array(); // Cache the attempt objects for reuse.. // We want to get the first one ready, so we can use it to print the info box. $firstattempt = reset($questionattempts); $quizattempt = quiz_attempt::create($firstattempt->quizattemptid); $quizattempts[$firstattempt->quizattemptid] = $quizattempt; $student = $DB->get_record('user', array('id' => $quizattempt->get_userid())); $courseid = $quizattempt->get_courseid(); $picture = $OUTPUT->user_picture($student, array('courseid' => $courseid)); $url = $CFG->wwwroot . '/user/view.php?id=' . $student->id . '&course=' . $courseid; $rows[] = '<tr> <th scope="row" class="cell">' . $picture . '</th> <td class="cell"> <a href="' . $url . '">' . fullname($student, true) . '</a> </td> </tr>'; // Now output the summary table, if there are any rows to be shown. if (!empty($rows)) { $output .= '<table class="generaltable generalbox quizreviewsummary"><tbody>' . "\n"; $output .= implode("\n", $rows); $output .= "\n</tbody></table>\n"; } foreach ($questionattempts as $questionattempt) { // Everything should already be in the right order as a nested array. // N.B. Using the proper quiz functions in an attempt to make this more robust // against future changes. if (!isset($quizattempts[$questionattempt->quizattemptid])) { $quizattempt = quiz_attempt::create($questionattempt->quizattemptid); $quizattempts[$questionattempt->quizattemptid] = $quizattempt; } else { $quizattempt = $quizattempts[$questionattempt->quizattemptid]; } // Log this review. $attemptid = $quizattempt->get_attemptid(); add_to_log($quizattempt->get_courseid(), 'quiz', 'review', 'reviewquestion.php?attempt=' . $attemptid . '&question=' . $params['questionid'], $quizattempt->get_quizid(), $quizattempt->get_cmid()); // Now make the actual markup to show one question plus commenting/grading stuff. $output .= $quizattempt->render_question_for_commenting($questionattempt->slot); } $output .= html_writer::start_tag('div'); $output .= html_writer::empty_tag('input', array('type' => 'submit', 'value' => 'Save')); foreach ($params as $name => $value) { $output .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => $name, 'value' => $value)); } $output .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'sesskey', 'value' => sesskey())); $output .= html_writer::end_tag('div'); $output .= html_writer::end_tag('form'); return $output; }
/** * Process and save the data from the feedback form. Mostly lifted from * $assignmentinstance->process_feedback(). * * @param object $data from the feedback form * @param $params * @return string */ public function process_data($data, $params) { global $CFG, $DB; // TODO validate data. require_once $CFG->libdir . '/gradelib.php'; require_once "{$CFG->dirroot}/repository/lib.php"; // For save and next, we need to know the userid to save, and the userid to go // We use a new hidden field in the form, and set it to -1. If it's set, we use this // as the userid to store. // This seems to be something that the pop up javascript will change in the normal run of // things. Normally it will be the -1 default. if ((int) $data->saveuserid !== -1) { $data->userid = $data->saveuserid; } if (!empty($data->cancel)) { // User hit cancel button. return 'cancelled'; } // Get DB records. $coursemodule = $DB->get_record('course_modules', array('id' => $params['coursemoduleid']), '*', MUST_EXIST); $course = $DB->get_record('course', array('id' => $coursemodule->course), '*', MUST_EXIST); $assignment = $DB->get_record('assignment', array('id' => $coursemodule->instance), '*', MUST_EXIST); /* @var stdClass[] $grading_info */ $grading_info = grade_get_grades($coursemodule->course, 'mod', 'assignment', $assignment->id, $data->userid); $submission = $DB->get_record('assignment_submissions', array('assignment' => $assignment->id, 'userid' => $data->userid), '*', MUST_EXIST); $user = $DB->get_record('user', array('id' => $data->userid), '*', MUST_EXIST); $assignmentinstance = $this->get_assignment_instance($assignment, $coursemodule, $course); // If 'revert to draft' has been clicked, we want a confirm button only. // We don't want to return yet because the main use case is to comment/grade and then // ask the student to improve. if (!empty($data->unfinalize) || !empty($data->revertbutton)) { $this->unfinalise_submission($submission, $assignment, $coursemodule, $course); } if (!$grading_info) { return 'Could not retrieve grading info.'; } // Check to see if grade has been locked or overridden. If so, we can't save anything. if ($grading_info->items[0]->grades[$data->userid]->locked || $grading_info->items[0]->grades[$data->userid]->overridden) { return 'Grade is locked or overridden'; } // Advanced grading if enabled. From assignment_base->validate_and_process_feedback(). // Sort out the form ready to tell it to display. list($mformdata, $advancedgradingwarning) = $this->get_mform_data_object($course, $assignment, $submission, $user, $coursemodule, $assignmentinstance); $submitform = new block_ajax_marking_assignment_form(block_ajax_marking_form_url($params), $mformdata); $submitform->set_data($mformdata); if ($submitform->is_submitted() || !empty($data->revertbutton)) { // Possibly redundant. // Form was submitted (= a submit button other than 'cancel' or 'next' has been // clicked). if (!$submitform->is_validated()) { return 'form not validated'; } /* @var gradingform_instance $gradinginstance */ $gradinginstance = $submitform->use_advanced_grading(); // Preprocess advanced grading here. if ($gradinginstance) { $formdata = $submitform->get_data(); // Create submission if it did not exist yet because we need submission->id for // storing the grading instance. $advancedgrading = $formdata->advancedgrading; // Calculates the gradebook grade based on the rubrics. $data->xgrade = $gradinginstance->submit_and_get_grade($advancedgrading, $submission->id); } } // Save outcomes if necessary. if (!empty($CFG->enableoutcomes)) { $assignmentinstance->process_outcomes($data->userid); } $submission = $this->save_submission($submission, $data); if (!$submission) { return 'Problem saving feedback'; } // Trigger grade event to update gradebook. $assignment->cmidnumber = $coursemodule->id; assignment_update_grades($assignment, $data->userid); add_to_log($coursemodule->course, 'assignment', 'update grades', 'submissions.php?id=' . $coursemodule->id . '&user='******''; }
/** * Makes the grading interface for the pop up. Robbed from /mod/assign/locallib.php * line 1583ish - view_single_grade_page(). * * @param array $params From $_GET * @param object $coursemodule The coursemodule object that the user has been authenticated * against * @param bool $data * @throws coding_exception * @global $PAGE * @global stdClass $CFG * @global moodle_database $DB * @global $OUTPUT * @global stdClass $USER * @return string */ public function grading_popup($params, $coursemodule, $data = false) { global $PAGE, $CFG, $DB; $modulecontext = context_module::instance($coursemodule->id); $course = $DB->get_record('course', array('id' => $coursemodule->course)); $coursecontext = context_course::instance($course->id); $assign = new assign($modulecontext, $coursemodule, $course); /* @var mod_assign_renderer $renderer */ $renderer = $PAGE->get_renderer('mod_assign'); $output = ''; // Include grade form. require_once $CFG->dirroot . '/mod/assign/gradeform.php'; // Need submit permission to submit an assignment. require_capability('mod/assign:grade', $modulecontext); /* Pinched from private method assign::get_grading_userid_list() */ $filter = get_user_preferences('assign_filter', ''); $table = new assign_grading_table($assign, 0, $filter, 0, false); $useridlist = $table->get_column_data('userid'); $userid = $params['userid']; $rownum = 0; foreach ($useridlist as $key => $useridinlist) { if ($useridinlist == $userid) { $rownum = $key; reset($useridlist); // Just in case. break; } } $last = false; if ($rownum == count($useridlist) - 1) { $last = true; } $user = $DB->get_record('user', array('id' => $userid)); if ($user) { $output .= $renderer->render(new assign_user_summary($user, $course->id, has_capability('moodle/site:viewfullnames', $coursecontext))); } $submission = $DB->get_record('assign_submission', array('assignment' => $assign->get_instance()->id, 'userid' => $userid)); // Get the current grade. Pinched from assign::get_user_grade(). $grade = $DB->get_record('assign_grades', array('assignment' => $assign->get_instance()->id, 'userid' => $userid)); // Pinched from assign::is_graded(). $isgraded = !empty($grade) && $grade->grade !== null && $grade->grade >= 0; if ($assign->can_view_submission($userid)) { $gradelocked = $grade && $grade->locked || $assign->grading_disabled($userid); $widget = new assign_submission_status($assign->get_instance()->allowsubmissionsfromdate, $assign->get_instance()->alwaysshowdescription, $submission, $assign->is_any_submission_plugin_enabled(), $gradelocked, $isgraded, $assign->get_instance()->duedate, $assign->get_submission_plugins(), $assign->get_return_action(), $assign->get_return_params(), $assign->get_course_module()->id, assign_submission_status::GRADER_VIEW, false, false); $output .= $renderer->render($widget); } if ($grade) { $data = new stdClass(); if ($grade->grade !== null && $grade->grade >= 0) { $data->grade = format_float($grade->grade, 2); } } else { $data = new stdClass(); $data->grade = ''; } // Now show the grading form. $customdata = array('useridlist' => $useridlist, 'rownum' => $rownum, 'last' => $last); $mform = new mod_assign_grade_form(block_ajax_marking_form_url($params), array($assign, $data, $customdata), 'post', '', array('class' => 'gradeform')); $output .= $renderer->render(new assign_form('gradingform', $mform)); $assign->add_to_log('view grading form', get_string('viewgradingformforstudent', 'assign', array('id' => $user->id, 'fullname' => fullname($user)))); return $output; }