/**
* Saves the current state of the question session to the database
*
* The state object representing the current state of the session for the
* question is saved to the question_states table with ->responses[''] saved
* to the answer field of the database table. The information in the
* question_sessions table is updated.
* The question type specific data is then saved.
* @return mixed           The id of the saved or updated state or false
* @param object $question The question for which session is to be saved.
* @param object $state    The state information to be saved. In particular the
*                         most recent responses are in ->responses. The object
*                         is updated to hold the new ->id.
*/
function save_question_session($question, $state)
{
    global $QTYPES, $DB;
    // Check if the state has changed
    if (!$state->changed && isset($state->id)) {
        if (isset($state->newflaggedstate) && $state->flagged != $state->newflaggedstate) {
            // If this fails, don't worry too much, it is not critical data.
            question_update_flag($state->questionsessionid, $state->newflaggedstate);
        }
        return $state->id;
    }
    // Set the legacy answer field
    $state->answer = isset($state->responses['']) ? $state->responses[''] : '';
    // Save the state
    if (!empty($state->update)) {
        // this forces the old state record to be overwritten
        $DB->update_record('question_states', $state);
    } else {
        if (!($state->id = $DB->insert_record('question_states', $state))) {
            unset($state->id);
            unset($state->answer);
            return false;
        }
    }
    // create or update the session
    if (!($session = $DB->get_record('question_sessions', array('attemptid' => $state->attempt, 'questionid' => $question->id)))) {
        $session = new stdClass();
        $session->attemptid = $state->attempt;
        $session->questionid = $question->id;
        $session->newest = $state->id;
        // The following may seem weird, but the newgraded field needs to be set
        // already even if there is no graded state yet.
        $session->newgraded = $state->id;
        $session->sumpenalty = $state->sumpenalty;
        $session->manualcomment = $state->manualcomment;
        $session->flagged = !empty($state->newflaggedstate);
        if (!$DB->insert_record('question_sessions', $session)) {
            return false;
        }
    } else {
        $session->newest = $state->id;
        if (question_state_is_graded($state) or $state->event == QUESTION_EVENTOPEN) {
            // this state is graded or newly opened, so it goes into the lastgraded field as well
            $session->newgraded = $state->id;
            $session->sumpenalty = $state->sumpenalty;
            $session->manualcomment = $state->manualcomment;
        } else {
            $session->manualcomment = $session->manualcomment;
        }
        $session->flagged = !empty($state->newflaggedstate);
        if (!$DB->update_record('question_sessions', $session)) {
            return false;
        }
    }
    unset($state->answer);
    // Save the question type specific state information and responses
    if (!$QTYPES[$question->qtype]->save_session_and_responses($question, $state)) {
        return false;
    }
    // Reset the changed flag
    $state->changed = false;
    return $state->id;
}
require_once $CFG->libdir . '/questionlib.php';
// Parameters
$sessionid = required_param('qsid', PARAM_INT);
$attemptid = required_param('aid', PARAM_INT);
$questionid = required_param('qid', PARAM_INT);
$newstate = required_param('newstate', PARAM_BOOL);
$checksum = required_param('checksum', PARAM_ALPHANUM);
// Check user is logged in.
require_login();
// Check the sesskey.
if (!confirm_sesskey()) {
    echo 'sesskey failure';
}
// Check the checksum - it is very hard to know who a question session belongs
// to, so we require that checksum parameter is matches an md5 hash of the
// three ids and the users username. Since we are only updating a flag, that
// probably makes it sufficiently difficult for malicious users to toggle
// other users flags.
if ($checksum != md5($attemptid . "_" . $USER->secret . "_" . $questionid . "_" . $sessionid)) {
    echo 'checksum failure';
}
// Check that the requested session really exists
$questionsession = $DB->get_record('question_sessions', array('id' => $sessionid, 'attemptid' => $attemptid, 'questionid' => $questionid));
if (!$questionsession) {
    echo 'invalid ids';
}
// Now change state
if (!question_update_flag($sessionid, $newstate)) {
    echo 'update failed';
}
echo 'OK';