/**
  * 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;
 }